<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Eurkon</title>
    <link>https://blog.eurkon.com/</link>
    
    <image>
      <url>https://www.gravatar.com/avatar/e1552ee12e1548c30208f62a1e553629</url>
      <title>Eurkon</title>
      <link>https://blog.eurkon.com/</link>
    </image>
    
    <atom:link href="https://blog.eurkon.com/rss.xml" rel="self" type="application/rss+xml"/>
    
    <description>在这里我将记录学习过程中的笔记、分享一些经验与想法。希望能够帮助到您！</description>
    <pubDate>Tue, 30 May 2023 01:00:00 GMT</pubDate>
    <generator>http://hexo.io/</generator>
    
    <item>
      <title>ECharts 帕累托图</title>
      <link>https://blog.eurkon.com/post/b0188c87.html</link>
      <guid>https://blog.eurkon.com/post/b0188c87.html</guid>
      <pubDate>Tue, 30 May 2023 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 600px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 600px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': '省份',    'data': [         "广东",        "江苏",        "山东",        "浙江",        "河南",        "湖北",        "四川",        "福建",        "安徽",        "湖南",        "河北",        "江西",        "云南",        "陕西",        "辽宁",        "贵州",        "山西",        "吉林",        "甘肃",        "海南",        "青海",    ]  },  {    'name': '金额',    'data': [      139345,      128103,      124647,      104095,      87250,      70391,      68517,      67878,      64549,      55118,      51985,      47924,      37205,      33631,      28046,      25162,      24023,      13857,      9212,      6312,      5206,    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  legend: true,  legendPosition: 'bottom',}var container = document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar lineData = []var sum = eval(data[1].data.join('+'))var csum = 0for (let i = 0; i < rowNum; i++) {  csum += data[1].data[i]  lineData.push({    'name': data[0].data[i],    'value': (csum / sum * 100).toFixed(2)  })}var option = {  color: config.colors,  tooltip: {    trigger: 'axis',    confine: true  },  xAxis: {    type: 'category',    data: data[0].data  },  yAxis: [    {      type: 'value',      splitNumber: 5    },    {      type: 'value',      axisLabel: { formatter: '{value}%' },    }  ],  series: [    {      name: data[1].name,      type: 'bar',      data: data[1].data    },    {      name: '累计百分比',      type: 'line',      data: lineData,      tooltip: { valueFormatter: (value) => value + '%' },      yAxisIndex: 1,      smooth: true    }  ]};chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/b0188c87.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 子弹图</title>
      <link>https://blog.eurkon.com/post/620d6870.html</link>
      <guid>https://blog.eurkon.com/post/620d6870.html</guid>
      <pubDate>Thu, 27 Apr 2023 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 480px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 480px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {      'name': '平台',      'data': [        '淘宝',        '天猫',        '京东',        '拼多多',        '小红书',        '微博'      ]    },    {      'name': '实际值',      'data': [        160,        145,        160,        134,        121,        179,      ]    },    {      'name': '目标值',      'data': [        115,        172,        135,        163,        150,        198,      ]    }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  legend: true,  legendPosition: 'bottom',}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar option = {  color: config.colors,  tooltip: {    confine: true,    trigger: 'axis'  },  legend: {    show: config.legend,    top: config.legendPosition,    data: [data[1].name, data[2].name],    icon: 'roundRect'  },  yAxis: {    type: 'category',    data: data[0].data  },  xAxis: { type: 'value' },  series: [    {      name: data[1].name,      data: data[1].data,      type: 'bar',    },    {      name: data[2].name,      type: 'scatter',      symbol: 'rect',      symbolSize: ['6', document.getElementById("container").offsetHeight * .6 / rowNum],      data: data[2].data    }  ]};chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/620d6870.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 径向条形图</title>
      <link>https://blog.eurkon.com/post/22774ddc.html</link>
      <guid>https://blog.eurkon.com/post/22774ddc.html</guid>
      <pubDate>Wed, 26 Apr 2023 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 600px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 600px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': '平台',    'data': [      '淘宝',      '天猫',      '京东',      '拼多多',      '小红书',      '微博'    ]  },  {    'name': '数值',    'data': [      7732,      6193,      6160,      4916,      3811,      2286    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  legend: true,  legendPosition: 'bottom',}var container = document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar option = {  color: config.colors,  tooltip: { confine: true },  legend: {    show: config.legend,    top: config.legendPosition,    data: data[0].data  },  polar: { radius: ['20%', '80%'] },  angleAxis: {    show: false,    // 最小值是0°处的数值，最大值是360°处的数值，避免出现弧度为0和2PI的数据    // min: value => (value.min > 1 ? value.min - value.max / 3 : 0),    max: (value) => (value.max * 4) / 3  },  radiusAxis: {    type: 'category',    inverse: true,    axisLabel: {      show: false    },    axisLine: {      show: false    },    axisTick: {      show: false    },    data: data[0].data  },  series: []};if (config.legend) {  for (let i = 0; i < rowNum; i++) {    let seriesData = new Array(rowNum);    seriesData[i] = data[1].data[i];    option.series.push({      name: data[0].data[i],      stack: 'bar',      type: 'bar',      coordinateSystem: 'polar',      tooltip: {        formatter: function (params) {          return data[1].name + '<br>' + params.marker + params.name + '<div style="float:right; margin-left: 30px;font-weight:bold;">' + params.data + '</div>'        }      },      roundCap: true, // 是否在环形柱条两侧使用圆弧效果。仅对极坐标系柱状图有效。      label: {        show: true,        formatter: '{b}: {c}',        position: 'start'      },      data: seriesData    })  }} else {  option.series.push({    name: data[1].name,    type: 'bar',    coordinateSystem: 'polar',    colorBy: 'data',    roundCap: true, // 是否在环形柱条两侧使用圆弧效果。仅对极坐标系柱状图有效。    label: {      show: true,      formatter: '{b}: {c}',      position: 'start'    },    data: data[1].data  });}chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/22774ddc.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 渐变折线图</title>
      <link>https://blog.eurkon.com/post/68739172.html</link>
      <guid>https://blog.eurkon.com/post/68739172.html</guid>
      <pubDate>Sun, 12 Mar 2023 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 600px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 600px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': '日期',    'data': [      '2000-06-05',      '2000-06-06',      '2000-06-07',      '2000-06-08',      '2000-06-09',      '2000-06-10',      '2000-06-11',      '2000-06-12',      '2000-06-13',      '2000-06-14',      '2000-06-15',      '2000-06-16',      '2000-06-17',      '2000-06-18',      '2000-06-19',      '2000-06-20',      '2000-06-21',      '2000-06-22',      '2000-06-23',      '2000-06-24',      '2000-06-25',      '2000-06-26',      '2000-06-27',      '2000-06-28',      '2000-06-29',      '2000-06-30',      '2000-07-01',      '2000-07-02',      '2000-07-03',      '2000-07-04',      '2000-07-05',      '2000-07-06',      '2000-07-07',      '2000-07-08',      '2000-07-09',      '2000-07-10',      '2000-07-11',      '2000-07-12',      '2000-07-13',      '2000-07-14',      '2000-07-15',      '2000-07-16',      '2000-07-17',      '2000-07-18',      '2000-07-19',      '2000-07-20',      '2000-07-21',      '2000-07-22',      '2000-07-23',      '2000-07-24'    ]  },  {    'name': 'Fake Data',    'data': [      116,      129,      135,      86,      73,      85,      73,      68,      92,      130,      245,      139,      115,      111,      309,      206,      137,      128,      85,      94,      71,      106,      84,      93,      85,      73,      83,      125,      107,      82,      44,      72,      106,      107,      66,      91,      92,      113,      107,      131,      111,      64,      69,      88,      77,      83,      111,      57,      55,      60    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  legend: true,  legendPosition: 'bottom',}var container = document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar linearData = [  {    offset: 0.6,    color: 'red'  },  {    offset: 0.8,    color: 'gold'  },  {    offset: 1,    color: 'green'  }];var linear = [];for (let i = 0; i < linearData.length - 1; i++) {  linear.push(linearData[i]);  linear.push({    offset: linearData[i].offset + 0.00000001,    color: linearData[i + 1].color  });}var option = {  tooltip: {    trigger: 'axis',    confine: true  },  xAxis: [    {      type: 'category',      boundaryGap: false,      data: data[0].data,      gridIndex: 0    },    {      type: 'category',      boundaryGap: false,      data: data[0].data,      gridIndex: 1    },    {      type: 'category',      boundaryGap: false,      data: data[0].data,      gridIndex: 2    },    {      type: 'category',      boundaryGap: false,      data: data[0].data,      gridIndex: 3    }  ],  grid: [    {      top: '3%',      height: '18%'    },    {      top: '28%',      height: '18%'    },    {      top: '53%',      height: '18%'    },    {      top: '78%',      height: '18%'    }  ],  yAxis: [    {      type: 'value'    },    {      type: 'value',      gridIndex: 1    },    {      type: 'value',      gridIndex: 2    },    {      type: 'value',      gridIndex: 3    }  ],  visualMap: [    {      show: false,      type: 'piecewise',      seriesIndex: 2,      pieces: [        {          min: 200,          color: 'green'        },        {          min: 100,          max: 200,          color: 'gold'        },        {          max: 100,          color: 'red'        }      ],      outOfRange: {        color: '#999'      }    },    {      show: false,      type: 'continuous',      seriesIndex: 3,      color: ['green', 'gold', 'red']    }  ],  series: [    {      name: 'Fake Data1',      type: 'line',      symbol: 'none',      lineStyle: {        opacity: 0      },      itemStyle: {        color: new echarts.graphic.LinearGradient(0, 0, 1, 0, linear)      },      data: data[1].data    },    {      name: 'Fake Data2',      type: 'line',      symbol: 'none',      itemStyle: {        color: new echarts.graphic.LinearGradient(0, 0, 1, 0, linearData)      },      areaStyle: {        color: new echarts.graphic.LinearGradient(0, 0, 1, 0, linearData)      },      xAxisIndex: 1,      yAxisIndex: 1,      data: data[1].data    },    {      name: 'Fake Data3',      type: 'line',      symbol: 'none',      xAxisIndex: 2,      yAxisIndex: 2,      data: data[1].data    },    {      name: 'Fake Data4',      type: 'line',      symbol: 'none',      xAxisIndex: 3,      yAxisIndex: 3,      data: data[1].data    },    {      name: 'Fake Data1',      type: 'line',      tooltip: { show: false },      symbol: 'none',      lineStyle: {        opacity: 0      },      areaStyle: {        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [          {            offset: 0,            color: linearData[0].color          },          {            offset: 1,            color: linearData[0].color          }        ])      },      data: data[1].data        .slice(0, Math.floor(rowNum * linearData[0].offset))        .concat(          new Array(Math.floor(rowNum * (1 - linearData[0].offset))).fill('')        )    },    {      name: 'Fake Data1',      tooltip: { show: false },      type: 'line',      symbol: 'none',      lineStyle: {        opacity: 0      },      areaStyle: {        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [          {            offset: 0,            color: linearData[1].color          },          {            offset: 1,            color: linearData[1].color          }        ])      },      data: new Array(Math.floor(rowNum * linearData[0].offset - 1))        .fill('')        .concat(          data[1].data            .slice(              Math.floor(rowNum * linearData[0].offset) - 1,              Math.floor(rowNum * linearData[1].offset)            )            .concat(              new Array(Math.floor(rowNum * (1 - linearData[1].offset))).fill(                ''              )            )        )    },    {      name: 'Fake Data1',      tooltip: { show: false },      type: 'line',      symbol: 'none',      lineStyle: {        opacity: 0      },      areaStyle: {        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [          {            offset: 0,            color: linearData[2].color          },          {            offset: 1,            color: linearData[2].color          }        ])      },      data: new Array(Math.floor(rowNum * linearData[1].offset) - 1)        .fill('')        .concat(          data[1].data            .slice(              Math.floor(rowNum * linearData[1].offset) - 1,              Math.floor(rowNum * linearData[2].offset)            )            .concat(new Array((rowNum / 5) * 4).fill(''))        )    }  ]};chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/68739172.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>数仓设计与 ETL 规范</title>
      <link>https://blog.eurkon.com/post/1c85bfc3.html</link>
      <guid>https://blog.eurkon.com/post/1c85bfc3.html</guid>
      <pubDate>Sat, 31 Dec 2022 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;数仓设计&quot;&gt;&lt;a href=&quot;#数仓设计&quot; class=&quot;headerlink&quot; title=&quot;数仓设计&quot;&gt;&lt;/a&gt;数仓设计&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;划分ETL阶段工作重心，便于管理&lt;/li&gt;
&lt;li&gt;降低开发和维护成本&lt;/li&gt;
&lt;li&gt;减少需求变化带来的冲击</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="数仓设计"><a href="#数仓设计" class="headerlink" title="数仓设计"></a>数仓设计</h2><ul><li>划分ETL阶段工作重心，便于管理</li><li>降低开发和维护成本</li><li>减少需求变化带来的冲击</li><li>便于数据问题追踪</li></ul><h3 id="数仓分层规范"><a href="#数仓分层规范" class="headerlink" title="数仓分层规范"></a>数仓分层规范</h3><div class="table-container"><table><thead><tr><th>层级</th><th>层级全拼</th><th>中文说明</th><th>层级描述</th></tr></thead><tbody><tr><td>DIM</td><td>dimension</td><td>维度层</td><td>该层为维度数据，维度是对具体分析对象的分析角度，维度要具备丰富的属性，历史信息的可追溯性，对通用的维表要保持一致性，避免维度二义性。</td></tr><tr><td>STG</td><td>stage</td><td>接口层</td><td>用于存储源系统主动推送到数仓的数据，存放未经过处理的原始数据至数据仓库系统，结构上与源系统保持一致，考虑后续可能追溯数据，因此对这一层不建议做过多的数据清洗工作，原封不动接入源数据即可，是数据仓库的数据准备区。</td></tr><tr><td>ODS</td><td>operation data store</td><td>源数据存储层</td><td>用于存储数仓主动抽取源系统的数据，存放未经过处理的原始数据至数据仓库系统，结构上与源系统保持一致，考虑后续可能追溯数据，因此对这一层不建议做过多的数据清洗工作，原封不动接入源数据即可，是数据仓库的数据准备区。</td></tr><tr><td>DWD</td><td>data warehouse detail</td><td>明细粒度事实层</td><td>以业务过程作为建模驱动，基于每个具体的业务过程特点，构建最细粒度的明细层事实表。也可以将明细事实表的某些重要维度属性字段做适当冗余，即宽表化处理。</td></tr><tr><td>DWS</td><td>data warehouse service</td><td>数据服务层（汇总）</td><td>以分析的主题对象作为建模驱动，基于上层的应用和产品的指标需求，构建公共粒度的汇总指标事实表。构建命名规范、口径一致的统计指标，为上层提供公共指标。</td></tr><tr><td>DM</td><td>data market</td><td>数据集市层</td><td>按业务主题组织高度汇总数据，产出通用的指标数据，统一指标计算口径和方法，避免指标冗余计算导致数据不一致。面向具体统计需求的；按业务主题划分;对数据进行高度汇总；尽量避免dm层表依赖dm表。</td></tr><tr><td>ADS</td><td>application data store</td><td>数据应用层</td><td>存放数据产品个性化的统计指标数据。 以宽表方式存放汇总拉通数据，关联维度表DIM与DWS层事实表，直接支持前端分析应用</td></tr></tbody></table></div><h3 id="各层物理表前缀"><a href="#各层物理表前缀" class="headerlink" title="各层物理表前缀"></a>各层物理表前缀</h3><div class="table-container"><table><thead><tr><th>层名</th><th>物理表前缀</th><th>备注</th></tr></thead><tbody><tr><td>STG</td><td>stg_</td><td>用于存储源系统主动推送到数仓或手工上传的数据</td></tr><tr><td>ODS</td><td>ods_</td><td>用于存储数仓主动抽取源系统的数据</td></tr><tr><td>DWD</td><td>dwd_</td><td>最细粒度的明细层事实表</td></tr><tr><td>DWS</td><td>dws_</td><td>公共粒度的汇总指标事实表</td></tr><tr><td>DIM</td><td>dim_</td><td>维度表</td></tr><tr><td>ADS</td><td>ads_</td><td>宽表方式存放汇总拉通数据</td></tr></tbody></table></div><h3 id="源系统命名规范"><a href="#源系统命名规范" class="headerlink" title="源系统命名规范"></a>源系统命名规范</h3><div class="table-container"><table><thead><tr><th>系统名称</th><th>全称命名</th><th>简称命名</th></tr></thead><tbody><tr><td>SAP系统</td><td>System Applications and Products</td><td>SAP</td></tr><tr><td>CRM系统</td><td>Customer Relationship Management</td><td>CRM</td></tr><tr><td>POS系统</td><td>Point Of Sale</td><td>POS</td></tr><tr><td>积分宝系统</td><td>Cumulative Scoring</td><td>CS</td></tr><tr><td>主数据系统</td><td>Master Data System</td><td>MD</td></tr><tr><td>DMS系统</td><td>Dealer Management System</td><td>DMS</td></tr><tr><td>填报系统</td><td>Filling System</td><td>FS</td></tr><tr><td>手工文档数据</td><td>Manual File</td><td>MF</td></tr></tbody></table></div><h3 id="更新频率及增量方式规范"><a href="#更新频率及增量方式规范" class="headerlink" title="更新频率及增量方式规范"></a>更新频率及增量方式规范</h3><div class="table-container"><table><thead><tr><th>增量方式</th><th>备注</th><th>更新频率</th><th>备注</th></tr></thead><tbody><tr><td>f</td><td>full-全量同步</td><td>h</td><td>hour 每小时更新</td></tr><tr><td>i</td><td>increment-增量同步</td><td>d</td><td>day 每天更新</td></tr><tr><td>i</td><td>increment-增量同步</td><td>w</td><td>week 每周更新</td></tr><tr><td>i</td><td>increment-增量同步</td><td>m</td><td>month 每月更新</td></tr></tbody></table></div><h3 id="数仓建表规范"><a href="#数仓建表规范" class="headerlink" title="数仓建表规范"></a>数仓建表规范</h3><div class="table-container"><table><thead><tr><th>数仓层级名称</th><th>数仓库表命名</th><th>审计字段命名</th><th>备注</th></tr></thead><tbody><tr><td>STG</td><td><code>schema_源系统_源系统表名_更新频率及增量方式</code><br>stg_tc_pos_item_info_df</td><td>create_time(创建时间)<br>modify_time(修改时间)<br>source(来源系统)<br>delete_flag(删除标记)</td><td>表名统一小写<br>行存表<br>指定分布键(建议主键)和分区字段</td></tr><tr><td>ODS</td><td><code>schema_源系统_源系统表名_更新频率及增量方式</code><br> ods_tc_pos_item_info_df</td><td>create_time(创建时间)<br>modify_time(修改时间)<br>source(来源系统)<br>delete_flag(删除标记)</td><td>表名统一小写<br>行存表<br>指定分布键(建议主键)和分区字段</td></tr><tr><td>DWD</td><td><code>schema_主题名称_交易表</code><br>dwd_tc_pos_item_info</td><td>create_time(创建时间)<br>modify_time(修改时间)<br>delete_flag(删除标记)</td><td>表名统一小写<br>列存表<br>指定分布键(建议主键)和分区字段</td></tr><tr><td>DWS</td><td><code>schema_主题名称_聚合方式</code><br>dws_sales_info</td><td>create_time(创建时间)<br>modify_time(修改时间)<br>delete_flag(删除标记)</td><td>表名统一小写<br>列存表<br>指定分布键(建议主键)和分区字段</td></tr><tr><td>ADS</td><td><code>schema_主题名称_聚合方式</code><br>ads_sales_info</td><td>create_time(创建时间)<br>modify_time(修改时间)<br>delete_flag(删除标记)</td><td>表名统一小写<br>列存表<br>指定分布键(建议主键)和分区字段</td></tr><tr><td>DIM</td><td><code>schema_维度名称</code><br>dim_product</td><td></td><td>表名统一小写<br>列存表<br>指定分布键(建议主键)和分区字段</td></tr></tbody></table></div><h2 id="ETL-设计规范"><a href="#ETL-设计规范" class="headerlink" title="ETL 设计规范"></a>ETL 设计规范</h2><h3 id="目录规范"><a href="#目录规范" class="headerlink" title="目录规范"></a>目录规范</h3><ul><li>目录层级划分能更清晰地对众多的开发程序进行分类和管理。</li><li>仅使用字母、数字、下划线组成，不能用任何特殊字符。</li><li>目录层级名称应遵循短小精悍原则，目录名统一全部大写。</li><li>目录层级规范适用于工作流、数据流和数据集的层级划分。</li></ul><h4 id="项目目录"><a href="#项目目录" class="headerlink" title="项目目录"></a>项目目录</h4><p>若项目上存在多个项目同时使用数据开发平台，那么建议在根目录下创建项目目录。如果一个项目中有多个子项目可以再进行子项目目录划分。</p><h4 id="环境目录"><a href="#环境目录" class="headerlink" title="环境目录"></a>环境目录</h4><p>一般情况下在项目不同的阶段会使用不同的环境开展对应的工作（较多的如开发环境、测试环境和生产环境等）对资源、账号和权限等进行隔离，以保证程序和数据的安全性。此时可不进行专门的环境目录区分。假如项目上数据开发平台只有一台服务器，那么建议设置不同的环境目录划分，具体环境目录由项目情况决定</p><ul><li>开发环境：DEV</li><li>测试环境：TEST</li><li>生产环境：PRD</li></ul><p>注意事项：对于不同的环境，为保证程序的一致性，可提前约定好开发内容迭代流程。</p><p>程序发布流程为 <code>DEV -&gt; TEST -&gt; PRD</code>，不允许从开发环境直接发布到生产环境的操作。</p><p>程序迭代流程为：</p><ol><li>DEV 环境进行程序开发，开发完成后（包括功能性测试）发布到 TEST</li><li>TEST 环境进行充分测试，发现问题后返回DEV环境进行程序修改</li><li>TEST 环境测试无误后发布到 PRD，由 ETL Leader 统一做集成和调度</li></ol><p>ETL任务文件放置目录以目标表所属分层为导向，即目标表属于ODS层，则任务文件存储位置为ODS目录。例如从SAP抽取数据到ODS和从CRM抽取数据到ODS层的ETL过程的所属数仓层级都是ODS层：</p><ul><li>数据落地层：ODS</li><li>公共维度层：DIM</li><li>数据明细层：DWD</li><li>数据聚合层：DWS</li><li>数据宽表层：ADS</li></ul><h4 id="数据来源目录"><a href="#数据来源目录" class="headerlink" title="数据来源目录"></a>数据来源目录</h4><p>根据不同的数据来源或源系统在每个环境的 ODS 层目录下都创建对应的目录，根据源业务系统名称进行命名。DWD及以上层级会进行数据整合，所以不需要再创建来源目录。以下为一些源业务系统名称（如有新增来源请在以下补充）：</p><ul><li>SAP系统：SAP</li><li>OMS(旺店通)：WDT</li><li>淘数据：TAOSJ</li><li>生意参谋：SYCM</li><li>达摩盘：DMP</li><li>淘宝API：TB</li><li>京东API：JD</li><li>唯品会API：WPH</li><li>FTP上传手工数据：FTP</li><li>CRM系统：CRM</li><li>DMS分销商：DMS</li><li>分销SAP系统：DIST_SAP</li><li>主数据系统：SFA</li><li>防窜系统：IS</li><li>积分宝系统：JFB</li><li>爬虫中间库：PgSQL中间库</li><li>填报系统：填报系统FS</li></ul><p>注：如有新增来源或系统的数据目录请在该文档中补充完善，以便于企业统一命名和文档传递。</p><h3 id="ETL-命名规范"><a href="#ETL-命名规范" class="headerlink" title="ETL 命名规范"></a>ETL 命名规范</h3><p>  本节所述的是具体的个体的命名规范，如果需要划分目录进行管理请参照上节的目录层级划分和目录命名规范进行设计。</p><h4 id="数据源命名规范"><a href="#数据源命名规范" class="headerlink" title="数据源命名规范"></a>数据源命名规范</h4><ol><li>数据源链接名称规范:统一以<code>“CNN_源库类型_数据库名_系统名”</code>。</li><li>在数据源描述中写：源库业务名称全称或说明，如有详细描述需写入描述中，中间以“；”号分隔。</li><li>数据源类型及对应的连接串如下：</li></ol><div class="table-container"><table><thead><tr><th>Connection Type</th><th>Connection Name</th></tr></thead><tbody><tr><td>ORACLE</td><td>CNN_ORA_SAPSR3_SAP</td></tr><tr><td>MYSQL</td><td>CNN_MySQL_CRM_CRM</td></tr><tr><td>GreenPlum</td><td>CNN_GP_ETL_DW</td></tr></tbody></table></div><h4 id="工作流命名规范"><a href="#工作流命名规范" class="headerlink" title="工作流命名规范"></a>工作流命名规范</h4><p>ETL 任务设计以目标表为导向，目标表名称与任务名称一一对应，原则上，一个目标表只对应一个 ETL 任务：</p><ol><li>ETL 工作流命名：命名方式是以 <code>workflow</code> 的缩写 <code>wf</code> 开头，后面跟上此工作流中的目标表名，最后跟上加载方式。<ul><li>增量任务命名格式：wf_目标表名_i</li><li>全量任务命名格式：wf_目标表名_f</li><li>删除任务命名格式：wf_目标表名_d</li></ul></li><li>非ETL类工作流命名：工作流执行的是一些表的更新、删除等非数据抽取的操作，以 wf 加上执行操作表的表名<ul><li>命名格式：wf_主要操作表名</li></ul></li><li>工作流描述中写入创建人、工作流的详细描述和说明，描述信息格式为：<code>“从源库 xxx T+1增量抽取 xxx 表数据到目标库 xxx 的 xxx 表中”</code>，工作流如果有逻辑处理需要简单说明处理逻辑，中间以“；”号分隔。</li></ol><h4 id="数据流命名规范"><a href="#数据流命名规范" class="headerlink" title="数据流命名规范"></a>数据流命名规范</h4><ol><li><p>ETL 数据流命名：命名方式是以 <code>dataflow</code> 的缩写 <code>df</code> 开头，后面加上数据流中操作的目标表名，最后跟上加载方式。</p><ul><li>增量任务命名格式：df_目标表名_i</li><li>全量任务命名格式：df_目标表名_f</li><li>删除任务命名格式：df_目标表名_d</li></ul></li><li><p>数据流描述中写入创建人、工作流的详细描述和说明，描述信息格式为：<code>“从源库 xxx T+1增量抽取 xxx 表数据到目标库 xxx 的 xxx 表中”</code>，工作流如果有逻辑处理需要简单说明处理逻辑，中间以“；”号分隔。</p></li></ol><h4 id="参数命名规范"><a href="#参数命名规范" class="headerlink" title="参数命名规范"></a>参数命名规范</h4><p>为了提高 ETL 程序的灵活性，需要设置一些公共的环境参数供任务在运行过程中动态使用，这些参数在运行过程中可以由运行人员根据实际的运行情况进行调整。</p><ol><li>参数命名以 <code>var</code> 开头，后面加上参数含义的简写 <code>var_参数含义</code></li><li>同一工作流中的参数名称不能重复</li><li>不同工作流中的相同参数名称要保持一致</li><li>数据开发平台中参数引用方式为 <code>$&#123;参数名&#125;</code></li></ol><h4 id="数据集命名规范"><a href="#数据集命名规范" class="headerlink" title="数据集命名规范"></a>数据集命名规范</h4><p>数据开发平台的数据集通常用做工作流中数据输出和数据流中的数据源，有以下几种应用场景：</p><ol><li>工作流中输出数据集用做数据流中的数据源</li><li>数据流中输出数据集用作业务层级进一步的分析</li><li>ETL 开发时非常复杂的数据逻辑中间可以使用数据集进行各个环节的逻辑验证</li><li>需要多次复用的 ETL 中间处理过程数据可以落入数据集</li></ol><p>数据集命名规范使用 <code>data set</code> 的首字母 <code>ds</code> 作为前缀，后面加上数据集对应的表名（数仓中经过统一规范命名之后的表名）或者数据集的业务含义，命名的方式为：<code>ds_数据集对应表名</code> 或者 <code>ds_数据集业务含义</code> 如：门店维表 ds_dim_store。</p><h2 id="ETL-开发规范"><a href="#ETL-开发规范" class="headerlink" title="ETL 开发规范"></a>ETL 开发规范</h2><h3 id="ODS-层开发规范"><a href="#ODS-层开发规范" class="headerlink" title="ODS 层开发规范"></a>ODS 层开发规范</h3><p>ODS 层 ETL 开发可以直接在工作流中完成。ODS 层开发过程相对较简单，只需在抽取数据的同时加上审计字段（见下表）和增量条件即可，可在源表取数的 SQL 中完成。</p><div class="table-container"><table><thead><tr><th>数仓层级</th><th>数据集命名设计规范</th><th>审计字段命名规范</th><th>备注</th></tr></thead><tbody><tr><td>ods</td><td><code>分层schema_源系统_源系统表名</code><br>示例：ods_wdt_ordernew</td><td>dw_create_dt(创建时间，默认当前时间) <br> dw_modify_dt(修改时间，默认当前时间) <br> data_source(数据源系统) <br> delete_flag(删除标记，默认为 0)</td><td>表名统一小写<br>指定去重主键</td></tr></tbody></table></div><h4 id="ODS-层创建目标表"><a href="#ODS-层创建目标表" class="headerlink" title="ODS 层创建目标表"></a>ODS 层创建目标表</h4><ol><li>字段类型：ODS层的字段类型参考数仓模型中的源跟目标表的字段类型对应关系</li><li>审计字段：添加数仓模型中 ODS 层所需要的4个审计字段</li><li>表类型：由于 ODS 层数据不进行分析，可以考虑建成行表</li><li>字符集：注意字符集统一，通常统一使用 UTF-8 字符集编码数据抽取</li></ol><h4 id="创建工作流"><a href="#创建工作流" class="headerlink" title="创建工作流"></a>创建工作流</h4><p>ODS 层目标表创建完成之后可以在数据开发平台对应的目录下创建工作流，工作流命名和描述要符合命名规范。</p><h4 id="获取源表数据"><a href="#获取源表数据" class="headerlink" title="获取源表数据"></a>获取源表数据</h4><p>拖入一个 SQL 组件到工作流中用于读取源表数据，主要工作是抽取数据，整个过程在不改变数据的本质情况下，做如下简单处理：</p><ol><li>添加审计字段</li><li>添加增量条件，通常ETL通过时间戳方式增量抽取数据</li><li>统一日期格式和数据清洗等</li></ol><p>原则上审计字段的值和增量条件的范围都需要使用参数进行统一规范。</p><p>数据在进入 ODS 层之前需要先进行清洗，结合数据梳理文档、指标体系文档等，确定清洗字段及清洗方式。将每个字段转换为遵循 ODS 标准的数据格式，对数据类型和数据格式进行转换，并对空字段赋予适当的缺省值，形成规整的数据结构。</p><p>源数据到 ODS 表需要先进行这部分操作，数据清洗规范：</p><div class="table-container"><table><thead><tr><th>问题分类</th><th>清洗数据</th><th>备注</th></tr></thead><tbody><tr><td>格式统一</td><td>时间格式不统一</td><td>可统一为 yyyy-mm-dd hh:mi:ss</td></tr><tr><td>格式统一</td><td>时间格式错误</td><td>可处理为固定值 9999-12-31 00:00:00</td></tr><tr><td>格式统一</td><td>日期格式不统一</td><td>可统一为 yyyy-mm-dd</td></tr><tr><td>格式统一</td><td>日期格式错误</td><td>可处理为固定值 9999-12-31</td></tr><tr><td>格式统一</td><td>门店编码 商品编码统一</td><td>创建编码对应关系表</td></tr><tr><td>格式统一</td><td>特殊符号 空格</td><td>可去除特殊符号，除非有特殊含义</td></tr><tr><td>一致性</td><td>逻辑关系检查</td><td>优先级低</td></tr><tr><td>一致性</td><td>字典值统一，形成通用字典表</td><td>优先级低</td></tr><tr><td>准确性</td><td>极值问题</td><td>优先级低</td></tr><tr><td>完整性</td><td>缺失值问题、null 值问题</td><td>优先级低</td></tr><tr><td>唯一性</td><td>重复值问题</td><td>优先级低</td></tr></tbody></table></div><h3 id="DWD-DIM-层开发规范"><a href="#DWD-DIM-层开发规范" class="headerlink" title="DWD/DIM 层开发规范"></a>DWD/DIM 层开发规范</h3><p>DWD 层的 ETL 开发也推荐使用工作流的方式实现，因为数据从 ODS 到 DWD 是进行表之间按照主题的合并与划分，也就是表之间的关联，并没有涉及到复杂的逻辑转换和聚合等操作，直接通过编写 SQL 的方式就能够快速高效地实现DWD 层的数据逻辑。</p><p>DWD 层的工作流程序通常也是采用一个读取数据的 SQL 节点和一个插入数据的 SQL 节点来实现。</p><p>本项目中在 ODS 层跟 DWD 层是在同一个数据库不同 schema 的前提下，具体实施过程中考虑到类实时（如 hourly）数据更新频率的时间限制要求，可以根据数据仓库的配置、性能和并发负载等综合能力情况，在工作流的单个 SQL 节点的非查询功能中通过类存储过程的方式直接编写插入查询语句 <code>INSERT … SELECT …</code> 的方式实现 DWD 层的 ETL 逻辑。</p><h3 id="DWS-层开发规范"><a href="#DWS-层开发规范" class="headerlink" title="DWS 层开发规范"></a>DWS 层开发规范</h3><p>DWS 层包括数据的纵向汇总和横向拉通两部分</p><p>事实表数据的纵向汇总就是在 DWD 层明细事实表的基础上直接进行各种不同粒度的汇总，每张汇总表只需要通过一个简单的分组汇总语句来实现，所以可以直接使用工作流来完成。</p><p>事实表汇总数据的横向拉通则会涉及到不同主题域数据的复杂关联和指标逻辑计算等内容，如指标值的差异、占比和同环比等。涉及到复杂关系的 ETL 过程建议使用数据开发平台的数据流，包括 DWS 层汇总后数据横向拉通和 ADS 层大宽表的 ETL 开发都使用数据流来完成。</p><p>DWS 拉通层开发步骤：</p><ol><li>在相应的目录下创建数据流</li><li>根据数据来源数量拖入相应的输入数据集组件，选择前面生成的数据集</li><li>拖入需要的数据关联和数据编辑组件进行逻辑编辑</li><li>使用插入数据算子（组件）把结果数据插入到目标表</li><li>拖入输出数据集算子把结果数据生成一个数据集，用作 ADS 层的数据源</li></ol><h3 id="ADS-层开发规范"><a href="#ADS-层开发规范" class="headerlink" title="ADS 层开发规范"></a>ADS 层开发规范</h3><p>按照数仓规范，ADS 层是存放 BI 前端分析应用的个性化的统计指标数据，以宽表方式存放汇总拉通数据，关联维度表 DIM 与 DWS 层事实表。</p><p>为方便分析师能在项目上线后自助的进行 ADS 层宽表的扩展，ADS 层统一通过数据流来开发的。</p><p>对于相同的数据源，比如数仓 GP 库，DIM/DWD/DWS 这几层采用工作流开发使用到的两个 SQL 节点（先获取、再插入）可以替换为 1 个 SQL 节点 - “非查询模式”。 这样就没有中间数据的落地过程，相当于 SQL 执行直接提交给 GP库上执行，因此性能会快一些，尤其是获取数据量较大的情况。因此对于如果没有注册数据集的情况，建议都使用“非查询”模式：<code>insert into xxx select * xxx</code></p><h3 id="作业集成、依赖和调度"><a href="#作业集成、依赖和调度" class="headerlink" title="作业集成、依赖和调度"></a>作业集成、依赖和调度</h3><p>集成类和调度类工作流命名：以 wf 加上集成的工作流层级、业务含义、调度频率等命名：wf_ods_sales_order_daily。</p><p>ETL 作业之间存在很多依赖，关系到相关数据表的加载顺序，本设计从两个方面体现 ETL 作业的依赖关系：一是每个工作流完成以后接着运行后续需要依赖该工作流完成的工作流，首先应等待第一个工作流完成之后再继续。二是将一组在同一时间开始运行并具有依赖关系的数据流组合在一起，开发成网状关系的工作流以供 ETL 的 schedule 调度用。</p><p>在 ETL 程序的抽取过程运行之前一定要保证其抽取的源数据已经准备就绪，根据不同的源系统可能需要分别采用不同的接口方式，通常采用时间约定相结合的方式，部分源系统可能增加数据就绪通知方式。</p><h4 id="工作流依赖和并发控制"><a href="#工作流依赖和并发控制" class="headerlink" title="工作流依赖和并发控制"></a>工作流依赖和并发控制</h4><p>本项目中的 ETL 工作流采用层级依赖的方式来控制数据的加载顺序，即在 ODS 层的工作流完成之后再运行 DWD 和 DIM 层的 ETL 程序，同时不同源系统的数据可以并行加载。</p><p>但是由于源系统来源表数量较多，基于读取源系统数据的并发量、ETL 调度程序执行的并发量和写入目标数据库的并发量考虑，并发量过大时会对源系统、数据开发平台和目标系统都会造成较大压力，所以本项目中调度遵循以下原则：</p><ol><li>不同源系统错开时间段进行数据抽取，以减轻开发平台和目标系统的负载压力</li><li>同一源系统的 ETL 程序本来没有依赖关系，通过设置依赖关系来控制并发数量，以减轻源系统的负载压力</li><li>同一时间点并行运行的 ETL 程序控制在 10 个以内</li></ol><h4 id="作业调度设置"><a href="#作业调度设置" class="headerlink" title="作业调度设置"></a>作业调度设置</h4><p>只有日常数据加载才需要考虑进程调度的问题。而对于初始数据加载，由于是一次性的工作，将采取手工启动加载的方式。</p><p>ETL 作业进程调度的功能比较单纯，就是在规定的时刻启动程序，并记录系统运行情况和运行结果。不同数据表的更新周期不同，因此，进程调度需要能够支持日周月等多种不同的启动周期，并通过设定启动时间来确定每个任务在何时启动运行。定时调度中可以选择每天、每周、每月等定时策略，每天的时间点支持多选；也可以选择间隔时间策略。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E6%96%87%E6%A1%A3/">文档</category>
      
      <category domain="https://blog.eurkon.com/tags/%E6%95%B0%E6%8D%AE%E4%BB%93%E5%BA%93/">数据仓库</category>
      
      
      <comments>https://blog.eurkon.com/post/1c85bfc3.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 生涯彩虹图</title>
      <link>https://blog.eurkon.com/post/553238e8.html</link>
      <guid>https://blog.eurkon.com/post/553238e8.html</guid>
      <pubDate>Sun, 06 Nov 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 600px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 600px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data1 = [  { value: 7, name: '0' },  { value: 7, name: '1' },  { value: 7, name: '2' },  { value: 6, name: '3' },  { value: 6, name: '4' },  { value: 6, name: '5' },  { value: 6, name: '6' },  { value: 6, name: '7' },  { value: 6, name: '8' },  { value: 6, name: '9' },  { value: 5, name: '10' },  { value: 5, name: '11' },  { value: 5, name: '12' },  { value: 4, name: '13' },  { value: 4, name: '14' },  { value: 4, name: '15' },  { value: 3, name: '16' },  { value: 3, name: '17' },  { value: 3, name: '18' },  { value: 2, name: '19' },  { value: 2, name: '20' },  { value: 2, name: '21' },  { value: 2, name: '22' },  { value: 1, name: '23' },  { value: 1, name: '24' },  { value: 1, name: '25' },  { value: 1, name: '26' },  { value: 1, name: '27' },  { value: 1, name: '28' },  { value: 1, name: '29' },  { value: 1, name: '30' },  { value: 1, name: '31' },  { value: 1, name: '32' },  { value: 1, name: '33' },  { value: 1, name: '34' },  { value: 1, name: '35' },  { value: 1, name: '36' },  { value: 1, name: '37' },  { value: 1, name: '38' },  { value: 1, name: '39' },  { value: 2, name: '40' },  { value: 2, name: '41' },  { value: 2, name: '42' },  { value: 2, name: '43' },  { value: 2, name: '44' },  { value: 2, name: '45' },  { value: 3, name: '46' },  { value: 3, name: '47' },  { value: 3, name: '48' },  { value: 3, name: '49' },  { value: 3, name: '50' },  { value: 3, name: '51' },  { value: 3, name: '52' },  { value: 3, name: '53' },  { value: 3, name: '54' },  { value: 3, name: '55' },  { value: 4, name: '56' },  { value: 4, name: '57' },  { value: 4, name: '58' },  { value: 4, name: '59' },  { value: 4, name: '60' },  { value: 4, name: '61' },  { value: 4, name: '62' },  { value: 4, name: '63' },  { value: 4, name: '64' },  { value: 4, name: '65' },  { value: 0, name: '66' },  { value: 0, name: '67' },  { value: 0, name: '68' },  { value: 0, name: '69' },  { value: 0, name: '70' },  { value: 0, name: '71' },  { value: 0, name: '72' },  { value: 0, name: '73' },  { value: 0, name: '74' },  { value: 0, name: '75' },  { value: 0, name: '76' },  { value: 0, name: '77' },  { value: 0, name: '78' },  { value: 0, name: '79' },  { value: 0, name: '80' }];var data2 = [  { value: 0, name: '0' },  { value: 0, name: '1' },  { value: 0, name: '2' },  { value: 3, name: '3' },  { value: 3, name: '4' },  { value: 3, name: '5' },  { value: 4, name: '6' },  { value: 4, name: '7' },  { value: 4, name: '8' },  { value: 5, name: '9' },  { value: 5, name: '10' },  { value: 5, name: '11' },  { value: 5, name: '12' },  { value: 5, name: '13' },  { value: 5, name: '14' },  { value: 5, name: '15' },  { value: 6, name: '16' },  { value: 6, name: '17' },  { value: 6, name: '18' },  { value: 3, name: '19' },  { value: 3, name: '20' },  { value: 3, name: '21' },  { value: 3, name: '22' },  { value: 1, name: '23' },  { value: 1, name: '24' },  { value: 1, name: '25' },  { value: 3, name: '26' },  { value: 3, name: '27' },  { value: 3, name: '28' },  { value: 2, name: '29' },  { value: 2, name: '30' },  { value: 1, name: '31' },  { value: 1, name: '32' },  { value: 1, name: '33' },  { value: 1, name: '34' },  { value: 1, name: '35' },  { value: 1, name: '36' },  { value: 1, name: '37' },  { value: 1, name: '38' },  { value: 1, name: '39' },  { value: 4, name: '40' },  { value: 4, name: '41' },  { value: 4, name: '42' },  { value: 4, name: '43' },  { value: 4, name: '44' },  { value: 4, name: '45' },  { value: 1, name: '46' },  { value: 1, name: '47' },  { value: 1, name: '48' },  { value: 1, name: '49' },  { value: 1, name: '50' },  { value: 1, name: '51' },  { value: 1, name: '52' },  { value: 1, name: '53' },  { value: 1, name: '54' },  { value: 1, name: '55' },  { value: 1, name: '56' },  { value: 1, name: '57' },  { value: 1, name: '58' },  { value: 1, name: '59' },  { value: 5, name: '60' },  { value: 5, name: '61' },  { value: 5, name: '62' },  { value: 5, name: '63' },  { value: 5, name: '64' },  { value: 5, name: '65' },  { value: 0, name: '66' },  { value: 0, name: '67' },  { value: 0, name: '68' },  { value: 0, name: '69' },  { value: 0, name: '70' },  { value: 0, name: '71' },  { value: 0, name: '72' },  { value: 0, name: '73' },  { value: 0, name: '74' },  { value: 0, name: '75' },  { value: 0, name: '76' },  { value: 0, name: '77' },  { value: 0, name: '78' },  { value: 0, name: '79' },  { value: 0, name: '80' }];var data3 = [  { value: 0, name: '0' },  { value: 0, name: '1' },  { value: 0, name: '2' },  { value: 0, name: '3' },  { value: 0, name: '4' },  { value: 0, name: '5' },  { value: 4, name: '6' },  { value: 4, name: '7' },  { value: 4, name: '8' },  { value: 4, name: '9' },  { value: 4, name: '10' },  { value: 4, name: '11' },  { value: 4, name: '12' },  { value: 3, name: '13' },  { value: 3, name: '14' },  { value: 3, name: '15' },  { value: 2, name: '16' },  { value: 2, name: '17' },  { value: 2, name: '18' },  { value: 5, name: '19' },  { value: 5, name: '20' },  { value: 5, name: '21' },  { value: 5, name: '22' },  { value: 2, name: '23' },  { value: 2, name: '24' },  { value: 2, name: '25' },  { value: 2, name: '26' },  { value: 2, name: '27' },  { value: 2, name: '28' },  { value: 2, name: '29' },  { value: 2, name: '30' },  { value: 3, name: '31' },  { value: 3, name: '32' },  { value: 3, name: '33' },  { value: 3, name: '34' },  { value: 3, name: '35' },  { value: 3, name: '36' },  { value: 3, name: '37' },  { value: 3, name: '38' },  { value: 3, name: '39' },  { value: 3, name: '40' },  { value: 4, name: '41' },  { value: 4, name: '42' },  { value: 4, name: '43' },  { value: 4, name: '44' },  { value: 4, name: '45' },  { value: 4, name: '46' },  { value: 4, name: '47' },  { value: 4, name: '48' },  { value: 4, name: '49' },  { value: 4, name: '50' },  { value: 5, name: '51' },  { value: 5, name: '52' },  { value: 5, name: '53' },  { value: 5, name: '54' },  { value: 5, name: '55' },  { value: 5, name: '56' },  { value: 5, name: '57' },  { value: 5, name: '58' },  { value: 5, name: '59' },  { value: 5, name: '60' },  { value: 5, name: '61' },  { value: 5, name: '62' },  { value: 5, name: '63' },  { value: 5, name: '64' },  { value: 5, name: '65' },  { value: 5, name: '66' },  { value: 5, name: '67' },  { value: 5, name: '68' },  { value: 5, name: '69' },  { value: 5, name: '70' },  { value: 5, name: '71' },  { value: 5, name: '72' },  { value: 5, name: '73' },  { value: 5, name: '74' },  { value: 5, name: '75' },  { value: 5, name: '76' },  { value: 5, name: '77' },  { value: 5, name: '78' },  { value: 5, name: '79' },  { value: 5, name: '80' }];var data4 = [  { value: 0, name: '0' },  { value: 0, name: '1' },  { value: 0, name: '2' },  { value: 0, name: '3' },  { value: 0, name: '4' },  { value: 0, name: '5' },  { value: 0, name: '6' },  { value: 0, name: '7' },  { value: 0, name: '8' },  { value: 0, name: '9' },  { value: 0, name: '10' },  { value: 0, name: '11' },  { value: 0, name: '12' },  { value: 0, name: '13' },  { value: 0, name: '14' },  { value: 0, name: '15' },  { value: 0, name: '16' },  { value: 0, name: '17' },  { value: 1, name: '18' },  { value: 1, name: '19' },  { value: 1, name: '20' },  { value: 1, name: '21' },  { value: 1, name: '22' },  { value: 1, name: '23' },  { value: 1, name: '24' },  { value: 1, name: '25' },  { value: 1, name: '26' },  { value: 1, name: '27' },  { value: 1, name: '28' },  { value: 1, name: '29' },  { value: 1, name: '30' },  { value: 1, name: '31' },  { value: 1, name: '32' },  { value: 1, name: '33' },  { value: 1, name: '34' },  { value: 1, name: '35' },  { value: 2, name: '36' },  { value: 2, name: '37' },  { value: 2, name: '38' },  { value: 2, name: '39' },  { value: 2, name: '40' },  { value: 2, name: '41' },  { value: 2, name: '42' },  { value: 2, name: '43' },  { value: 2, name: '44' },  { value: 2, name: '45' },  { value: 3, name: '46' },  { value: 3, name: '47' },  { value: 3, name: '48' },  { value: 3, name: '49' },  { value: 3, name: '50' },  { value: 3, name: '51' },  { value: 3, name: '52' },  { value: 3, name: '53' },  { value: 3, name: '54' },  { value: 3, name: '55' },  { value: 3, name: '56' },  { value: 3, name: '57' },  { value: 3, name: '58' },  { value: 3, name: '59' },  { value: 3, name: '60' },  { value: 3, name: '61' },  { value: 3, name: '62' },  { value: 3, name: '63' },  { value: 3, name: '64' },  { value: 3, name: '65' },  { value: 4, name: '66' },  { value: 4, name: '67' },  { value: 4, name: '68' },  { value: 4, name: '69' },  { value: 4, name: '70' },  { value: 2, name: '71' },  { value: 2, name: '72' },  { value: 2, name: '73' },  { value: 2, name: '74' },  { value: 2, name: '75' },  { value: 1, name: '76' },  { value: 1, name: '77' },  { value: 1, name: '78' },  { value: 1, name: '79' },  { value: 1, name: '80' }];var data5 = [  { value: 0, name: '0' },  { value: 0, name: '1' },  { value: 0, name: '2' },  { value: 0, name: '3' },  { value: 0, name: '4' },  { value: 0, name: '5' },  { value: 0, name: '6' },  { value: 0, name: '7' },  { value: 0, name: '8' },  { value: 0, name: '9' },  { value: 0, name: '10' },  { value: 0, name: '11' },  { value: 0, name: '12' },  { value: 0, name: '13' },  { value: 0, name: '14' },  { value: 0, name: '15' },  { value: 0, name: '16' },  { value: 0, name: '17' },  { value: 0, name: '18' },  { value: 0, name: '19' },  { value: 0, name: '20' },  { value: 0, name: '21' },  { value: 0, name: '22' },  { value: 3, name: '23' },  { value: 3, name: '24' },  { value: 3, name: '25' },  { value: 5, name: '26' },  { value: 5, name: '27' },  { value: 5, name: '28' },  { value: 5, name: '29' },  { value: 5, name: '30' },  { value: 4, name: '31' },  { value: 4, name: '32' },  { value: 4, name: '33' },  { value: 4, name: '34' },  { value: 4, name: '35' },  { value: 4, name: '36' },  { value: 4, name: '37' },  { value: 4, name: '38' },  { value: 4, name: '39' },  { value: 4, name: '40' },  { value: 3, name: '41' },  { value: 3, name: '42' },  { value: 3, name: '43' },  { value: 3, name: '44' },  { value: 3, name: '45' },  { value: 3, name: '46' },  { value: 3, name: '47' },  { value: 3, name: '48' },  { value: 3, name: '49' },  { value: 3, name: '50' },  { value: 2, name: '51' },  { value: 2, name: '52' },  { value: 2, name: '53' },  { value: 2, name: '54' },  { value: 2, name: '55' },  { value: 2, name: '56' },  { value: 2, name: '57' },  { value: 2, name: '58' },  { value: 2, name: '59' },  { value: 2, name: '60' },  { value: 1, name: '61' },  { value: 1, name: '62' },  { value: 1, name: '63' },  { value: 1, name: '64' },  { value: 1, name: '65' },  { value: 0, name: '66' },  { value: 0, name: '67' },  { value: 0, name: '68' },  { value: 0, name: '69' },  { value: 0, name: '70' },  { value: 0, name: '71' },  { value: 0, name: '72' },  { value: 0, name: '73' },  { value: 0, name: '74' },  { value: 0, name: '75' },  { value: 0, name: '76' },  { value: 0, name: '77' },  { value: 0, name: '78' },  { value: 0, name: '79' },  { value: 0, name: '80' }];var data6 = [  { value: 0, name: '0' },  { value: 0, name: '1' },  { value: 0, name: '2' },  { value: 0, name: '3' },  { value: 0, name: '4' },  { value: 0, name: '5' },  { value: 0, name: '6' },  { value: 0, name: '7' },  { value: 0, name: '8' },  { value: 0, name: '9' },  { value: 0, name: '10' },  { value: 0, name: '11' },  { value: 0, name: '12' },  { value: 0, name: '13' },  { value: 0, name: '14' },  { value: 0, name: '15' },  { value: 0, name: '16' },  { value: 0, name: '17' },  { value: 0, name: '18' },  { value: 0, name: '19' },  { value: 0, name: '20' },  { value: 0, name: '21' },  { value: 0, name: '22' },  { value: 0, name: '23' },  { value: 0, name: '24' },  { value: 0, name: '25' },  { value: 0, name: '26' },  { value: 0, name: '27' },  { value: 4, name: '28' },  { value: 4, name: '29' },  { value: 4, name: '30' },  { value: 3, name: '31' },  { value: 3, name: '32' },  { value: 3, name: '33' },  { value: 3, name: '34' },  { value: 3, name: '35' },  { value: 3, name: '36' },  { value: 3, name: '37' },  { value: 3, name: '38' },  { value: 3, name: '39' },  { value: 3, name: '40' },  { value: 3, name: '41' },  { value: 3, name: '42' },  { value: 3, name: '43' },  { value: 3, name: '44' },  { value: 3, name: '45' },  { value: 2, name: '46' },  { value: 2, name: '47' },  { value: 2, name: '48' },  { value: 2, name: '49' },  { value: 2, name: '50' },  { value: 2, name: '51' },  { value: 2, name: '52' },  { value: 2, name: '53' },  { value: 3, name: '54' },  { value: 3, name: '55' },  { value: 3, name: '56' },  { value: 3, name: '57' },  { value: 3, name: '58' },  { value: 3, name: '59' },  { value: 3, name: '60' },  { value: 3, name: '61' },  { value: 3, name: '62' },  { value: 3, name: '63' },  { value: 3, name: '64' },  { value: 3, name: '65' },  { value: 2, name: '66' },  { value: 2, name: '67' },  { value: 2, name: '68' },  { value: 2, name: '69' },  { value: 2, name: '70' },  { value: 1, name: '71' },  { value: 1, name: '72' },  { value: 1, name: '73' },  { value: 1, name: '74' },  { value: 1, name: '75' },  { value: 1, name: '76' },  { value: 1, name: '77' },  { value: 1, name: '78' },  { value: 1, name: '79' },  { value: 1, name: '80' }];var emptyData = [  { value: 0, name: '81' },  { value: 0, name: '82' },  { value: 0, name: '83' },  { value: 0, name: '84' },  { value: 0, name: '85' },  { value: 0, name: '86' },  { value: 0, name: '87' },  { value: 0, name: '88' },  { value: 0, name: '89' },  { value: 0, name: '90' },  { value: 0, name: '91' },  { value: 0, name: '92' },  { value: 0, name: '93' },  { value: 0, name: '94' },  { value: 0, name: '95' },  { value: 0, name: '96' },  { value: 0, name: '97' },  { value: 0, name: '98' },  { value: 0, name: '99' },  { value: 0, name: '100' },  { value: 0, name: '101' },  { value: 0, name: '102' },  { value: 0, name: '103' },  { value: 0, name: '104' },  { value: 0, name: '105' },  { value: 0, name: '106' },  { value: 0, name: '107' },  { value: 0, name: '108' },  { value: 0, name: '109' },  { value: 0, name: '110' },  { value: 0, name: '111' },  { value: 0, name: '112' },  { value: 0, name: '113' },  { value: 0, name: '114' },  { value: 0, name: '115' },  { value: 0, name: '116' },  { value: 0, name: '117' },  { value: 0, name: '118' },  { value: 0, name: '119' },  { value: 0, name: '120' },  { value: 0, name: '121' },  { value: 0, name: '122' },  { value: 0, name: '123' },  { value: 0, name: '124' },  { value: 0, name: '125' },  { value: 0, name: '126' },  { value: 0, name: '127' },  { value: 0, name: '128' },  { value: 0, name: '129' },  { value: 0, name: '130' },  { value: 0, name: '131' },  { value: 0, name: '132' },  { value: 0, name: '133' },  { value: 0, name: '134' },  { value: 0, name: '135' },  { value: 0, name: '136' },  { value: 0, name: '137' },  { value: 0, name: '138' },  { value: 0, name: '139' },  { value: 0, name: '140' },  { value: 0, name: '141' },  { value: 0, name: '142' },  { value: 0, name: '143' },  { value: 0, name: '144' },  { value: 0, name: '145' },  { value: 0, name: '146' },  { value: 0, name: '147' },  { value: 0, name: '148' },  { value: 0, name: '149' },  { value: 0, name: '150' },  { value: 0, name: '151' },  { value: 0, name: '152' },  { value: 0, name: '153' },  { value: 0, name: '154' },  { value: 0, name: '155' },  { value: 0, name: '156' },  { value: 0, name: '157' },  { value: 0, name: '158' },  { value: 0, name: '159' },  { value: 0, name: '160' }];var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#4379CE', '#D4E1F7', '#FC8602', '#009688', '#E6122F', '#72B5EB', '#78CDED', '#AC9AE2', '#FAC36F', '#FD7F7F']}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' }) var option = {  series: [    {      type: 'gauge',      min: 0,      max: 80,      startAngle: 180,      endAngle: 0,      splitNumber: 16,      radius: '130%',      center: ['50%', '80%'],      axisLine: {        lineStyle: {          width: 8,          color: [            [0.113, '#FF6E76'],            [0.225, '#FDDD60'],            [0.438, '#80c38a'],            [0.75, '#58D9F9'],            [1, '#a653c6']          ]        }      },      splitLine: {        show: false      },      axisTick: {        show: false      },      axisLabel: {        fontSize: 20,        distance: -70,        // rotate: 'tangential',        formatter: function (value) {          if (value === 5) {            return '成长\n阶段';          } else if (value === 15) {            return '探索\n阶段';          } else if (value === 25) {            return '建立\n阶段';          } else if (value === 45) {            return '维持\n阶段';          } else if (value === 70) {            return '退出\n阶段';          }        }      },      detail: {        offsetCenter: [0, '-5%'],        valueAnimation: true,        formatter: '{value}',        color: '#000'      },      pointer: {        icon: 'path://M12.8,0.7l12,40.1H0.7L12.8,0.7z',        length: 16,        width: 16,        offsetCenter: [0, '-95%'],        itemStyle: {          color: 'auto'        }      },      data: [        {          value: new Date().getFullYear() - 1997,          name: '个人决定因素：心理、生物\n环境决定因素：历史、社会经济'        }      ]    },    {      type: 'gauge',      min: 0,      max: 80,      startAngle: '180',      endAngle: '0',      splitNumber: 16,      radius: '130%',      center: ['50%', '80%'],      axisLine: {        lineStyle: {          width: 0        }      },      splitLine: {        length: 8,        lineStyle: {          color: '#858585'        }      },      axisLabel: {        distance: 5,        fontSize: 14,        color: '#000'      },      pointer: {        show: true      }    },    {      name: '子女',      type: 'pie',      startAngle: 180,      radius: ['30%', '44%'],      center: ['50%', '80%'],      roseType: 'area',      emphasis: {        disabled: true      },      itemStyle: {        color: '#ec4e4f'      },      labelLine: {        show: false,        length: -30,        length2: 0      },      label: {        formatter: function (item) {          return item.name === '0' ? item.seriesName : '';        }      },      data: data1.concat(emptyData)    },    {      name: '学生',      type: 'pie',      startAngle: 180,      radius: ['45%', '59%'],      center: ['50%', '80%'],      roseType: 'area',      emphasis: {        disabled: true      },      itemStyle: {        color: '#f59851'      },      labelLine: {        show: false,        length: -30,        length2: 0      },      label: {        formatter: function (item) {          return item.name === '0' ? item.seriesName : '';        }      },      data: data2.concat(emptyData)    },    {      name: '休闲者',      type: 'pie',      startAngle: 180,      radius: ['60%', '74%'],      center: ['50%', '80%'],      roseType: 'area',      emphasis: {        disabled: true      },      itemStyle: {        color: '#ffc542'      },      labelLine: {        show: false,        length: -30,        length2: 0      },      label: {        formatter: function (item) {          return item.name === '0' ? item.seriesName : '';        }      },      data: data3.concat(emptyData)    },    {      name: '公民',      type: 'pie',      startAngle: 180,      radius: ['75%', '89%'],      center: ['50%', '80%'],      roseType: 'area',      emphasis: {        disabled: true      },      itemStyle: {        color: '#82c28a'      },      labelLine: {        show: false,        length: -30,        length2: 0      },      label: {        formatter: function (item) {          return item.name === '15' ? item.seriesName : '';        }      },      data: data4.concat(emptyData)    },    {      name: '工作者',      type: 'pie',      startAngle: 180,      radius: ['90%', '104%'],      center: ['50%', '80%'],      roseType: 'area',      emphasis: {        disabled: true      },      itemStyle: {        color: '#64c4d1'      },      labelLine: {        show: false,        length: -30,        length2: 0      },      label: {        formatter: function (item) {          return item.name === '20' ? item.seriesName : '';        }      },      data: data5.concat(emptyData)    },    {      name: '持家者',      type: 'pie',      startAngle: 180,      radius: ['105%', '115%'],      center: ['50%', '80%'],      roseType: 'area',      emphasis: {        disabled: true      },      itemStyle: {        color: '#0494f5'      },      labelLine: {        show: false,        length: -20,        length2: 0      },      label: {        formatter: function (item) {          return item.name === '25' ? item.seriesName : '';        }      },      data: data6.concat(emptyData)    }  ]};chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/553238e8.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Butterfly 文章增加段落序号</title>
      <link>https://blog.eurkon.com/post/70e521c2.html</link>
      <guid>https://blog.eurkon.com/post/70e521c2.html</guid>
      <pubDate>Mon, 31 Oct 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;又到了月末了，工作太忙，随便鸽一篇文章吧，一个简单 JS 实现的魔改，给文章增加段落序号，弃用原本 butterfly 自带的文章段落前缀符</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>又到了月末了，工作太忙，随便鸽一篇文章吧，一个简单 JS 实现的魔改，给文章增加段落序号，弃用原本 butterfly 自带的文章段落前缀符号，使用 JS 生成文章段落序号（与目录的段落序号保持一致）。</p><p>需开启目录段落序号的配置项，如果想不显示目录的段落序号，可以使用 CSS 隐藏。</p><p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Post</span></span><br><span class="line"><span class="comment"># --------------------------------------</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># toc (目錄)</span></span><br><span class="line"><span class="attr">toc:</span></span><br><span class="line">  <span class="attr">post:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">page:</span> <span class="literal">false</span></span><br><span class="line">  <span class="attr">number:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">expand:</span> <span class="literal">false</span></span><br><span class="line">  <span class="attr">style_simple:</span> <span class="literal">false</span> <span class="comment"># for post</span></span><br></pre></td></tr></table></figure></p><h2 id="添加自定义-JS"><a href="#添加自定义-JS" class="headerlink" title="添加自定义 JS"></a>添加自定义 JS</h2><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">postAddToc</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> postContent = <span class="built_in">document</span>.querySelector(<span class="string">&#x27;#post&gt;#article-container.post-content&#x27;</span>)</span><br><span class="line">  <span class="keyword">let</span> cardToc = <span class="built_in">document</span>.getElementById(<span class="string">&#x27;card-toc&#x27;</span>)</span><br><span class="line">  <span class="keyword">if</span> (postContent &amp;&amp; cardToc) &#123;</span><br><span class="line">    <span class="keyword">let</span> tocNumber = cardToc.getElementsByClassName(<span class="string">&#x27;toc-number&#x27;</span>)</span><br><span class="line">    <span class="keyword">let</span> tocLink = cardToc.getElementsByClassName(<span class="string">&#x27;toc-link&#x27;</span>)</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; tocLink.length; i++) &#123;</span><br><span class="line">      <span class="built_in">document</span>.getElementById(<span class="built_in">decodeURIComponent</span>(tocLink[i].attributes.href.value).slice(<span class="number">1</span>)).dataset.toc = tocNumber[i].innerText</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">postAddToc ()</span><br></pre></td></tr></table></figure></p><h2 id="添加自定义-CSS"><a href="#添加自定义-CSS" class="headerlink" title="添加自定义 CSS"></a>添加自定义 CSS</h2><p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h1</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h2</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h3</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h4</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h5</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h6</span> &#123;</span><br><span class="line">  <span class="attribute">padding-left</span>: <span class="number">0</span> <span class="meta">!important</span>; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h1</span><span class="selector-pseudo">::before</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h2</span><span class="selector-pseudo">::before</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h3</span><span class="selector-pseudo">::before</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h4</span><span class="selector-pseudo">::before</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h5</span><span class="selector-pseudo">::before</span>,</span><br><span class="line"><span class="selector-id">#post</span> <span class="selector-id">#article-container</span><span class="selector-class">.post-content</span> <span class="selector-tag">h6</span><span class="selector-pseudo">::before</span> &#123;</span><br><span class="line">  <span class="attribute">position</span>: relative;</span><br><span class="line">  <span class="attribute">content</span>: <span class="built_in">attr</span>(data-toc) <span class="string">&#x27; &#x27;</span>;</span><br><span class="line">  <span class="attribute">display</span>: inline;</span><br><span class="line">  <span class="attribute">font-family</span>: inherit;</span><br><span class="line">  <span class="attribute">font-size</span>: inherit;</span><br><span class="line">  <span class="attribute">line-height</span>: inherit;</span><br><span class="line">  <span class="attribute">margin-left</span>: <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.eurkon.com/tags/Butterfly/">Butterfly</category>
      
      
      <comments>https://blog.eurkon.com/post/70e521c2.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Butterfly Twikoo 评论热评</title>
      <link>https://blog.eurkon.com/post/364efc10.html</link>
      <guid>https://blog.eurkon.com/post/364efc10.html</guid>
      <pubDate>Tue, 13 Sep 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文教程主要针对 Hexo Butterfly 主题博客中的 Twikoo 评论，按本文逻辑或许可以获取其他类型的评论，本文不作探讨。&lt;/p</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文教程主要针对 Hexo Butterfly 主题博客中的 Twikoo 评论，按本文逻辑或许可以获取其他类型的评论，本文不作探讨。</p><p>看了其他小伙伴的 评论热评教程，觉得有点复杂，于是自己想了一个方法来实现这个功能。</p><ul><li>沿用其他模块中用到的 swiper 轮播</li><li>使用本地缓存保存 隐藏或显示 热评，刷新或切换页面时仍 隐藏或显示 热评</li><li>增加评论的城市和日期</li><li>页面提交评论后会立即更新热评数据</li><li>点击 <code>头像</code> 可访问网站</li><li>点击 <code>热评</code> 可跳转至评论区</li><li>点击 <code>评论内容</code> 可跳转至改评论</li></ul><p>此功能需关闭评论的懒加载 <code>_config.butterfly.yml =&gt; comments.lazyload: false</code>，否则需等页面活动至评论区才会加载。</p><p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">comments:</span></span><br><span class="line">  <span class="comment"># Up to two comments system, the first will be shown as default</span></span><br><span class="line">  <span class="comment"># Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42</span></span><br><span class="line">  <span class="attr">use:</span> <span class="comment"># Valine,Disqus</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">Twikoo</span></span><br><span class="line">  <span class="attr">text:</span> <span class="literal">true</span> <span class="comment"># Display the comment name next to the button</span></span><br><span class="line">  <span class="comment"># lazyload: The comment system will be load when comment element enters the browser&#x27;s viewport.</span></span><br><span class="line">  <span class="comment"># If you set it to true, the comment count will be invalid</span></span><br><span class="line">  <span class="attr">lazyload:</span> <span class="literal">false</span> </span><br><span class="line">  <span class="attr">count:</span> <span class="literal">true</span> <span class="comment"># Display comment count in post&#x27;s top_img</span></span><br><span class="line">  <span class="attr">card_post_count:</span> <span class="literal">false</span> <span class="comment"># Display comment count in Home Page</span></span><br></pre></td></tr></table></figure></p><h2 id="修改-twikoo-pug"><a href="#修改-twikoo-pug" class="headerlink" title="修改 twikoo.pug"></a>修改 twikoo.pug</h2><p>打开 Twikoo 评论文件 <code>\themes\butterfly\layout\includes\third-party\comments\twikoo.pug</code>，添加以下内容。</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">- const &#123; envId, region, option &#125; = theme.twikoo</span></span><br><span class="line"><span class="deletion">- const &#123; use, lazyload, count &#125; = theme.comments</span></span><br><span class="line"></span><br><span class="line">script.</span><br><span class="line">  (()=&gt;&#123;</span><br><span class="line">    const init = () =&gt; &#123;</span><br><span class="line">      twikoo.init(Object.assign(&#123;</span><br><span class="line">        el: &#x27;#twikoo-wrap&#x27;,</span><br><span class="line">        envId: &#x27;!&#123;envId&#125;&#x27;,</span><br><span class="line">        region: &#x27;!&#123;region&#125;&#x27;,</span><br><span class="line">        onCommentLoaded: function () &#123;</span><br><span class="line">          btf.loadLightbox(document.querySelectorAll(&#x27;#twikoo .tk-content img:not(.tk-owo-emotion)&#x27;))</span><br><span class="line"><span class="addition">+         setTimeout(function()&#123;</span></span><br><span class="line"><span class="addition">+           let tk_comment = document.querySelectorAll(&#x27;.tk-comments-container .tk-comment&#x27;)</span></span><br><span class="line"><span class="addition">+           if (tk_comment.length &gt; 0) &#123;</span></span><br><span class="line"><span class="addition">+             let html = `&lt;div class=&quot;swiper-wrapper&quot;&gt;`</span></span><br><span class="line"><span class="addition">+             for (let i = 0; i &lt; tk_comment.length; i++) &#123;</span></span><br><span class="line"><span class="addition">+               let tk_id = tk_comment[i].getAttribute(&#x27;id&#x27;) || &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+               let tk_nick = tk_comment[i].querySelector(&#x27;.tk-nick&#x27;)?.innerText || &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+               let tk_href = tk_comment[i].querySelector(&#x27;.tk-nick&#x27;)?.href || &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+               let tk_avatar = tk_comment[i].querySelector(&#x27;.tk-avatar-img&#x27;)?.src || &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+               let tk_time = tk_comment[i].querySelector(&#x27;.tk-time&#x27;)?.innerText || &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+               let tk_city = tk_comment[i].querySelector(&#x27;.tk-extras .tk-extra:first-child span:last-child&#x27;)?.innerText || &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+               let tk_content = tk_comment[i].querySelector(&#x27;.tk-content&gt;span:last-child&#x27;)?.innerHTML || &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+               tk_content = tk_content.replace(/\n/g, &#x27;&#x27;) // replace \n</span></span><br><span class="line"><span class="addition">+               tk_content = tk_content.replace(/&lt;blockquote&gt;.*?&lt;\/blockquote&gt;/gi, &#x27;&#x27;) // replace blockquote</span></span><br><span class="line"><span class="addition">+               tk_content = tk_content.replace(/&lt;pre.*?&lt;\/pre&gt;/gi, &#x27;[!&#123;_p(&quot;aside.card_newest_comments.code&quot;)&#125;]&#x27;) // replace code</span></span><br><span class="line"><span class="addition">+               html += `</span></span><br><span class="line"><span class="addition">+                 &lt;div class=&quot;swiper-slide&quot;&gt;</span></span><br><span class="line"><span class="addition">+                   &lt;div class=&quot;comment-barrage-item&quot;&gt;</span></span><br><span class="line"><span class="addition">+                     &lt;div class=&quot;barrage-info&quot;&gt;</span></span><br><span class="line"><span class="addition">+                       &lt;a class=&quot;barrage-title&quot; title=&quot;跳转至评论区&quot; href=&quot;#post-comment&quot;&gt;热评&lt;/a&gt;</span></span><br><span class="line"><span class="addition">+                       &lt;a href=&quot;$&#123;tk_href ? tk_href + &#x27;&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; title=&quot;访问 &#x27;+ tk_nick +&#x27;&quot;&#x27; : &#x27;javascript:void(0);&quot;&#x27;&#125;&gt;</span></span><br><span class="line"><span class="addition">+                         &lt;img class=&quot;barrage-avatar&quot; src=&quot;$&#123;tk_avatar&#125;&quot;&gt;</span></span><br><span class="line"><span class="addition">+                       &lt;/a&gt;</span></span><br><span class="line"><span class="addition">+                       &lt;span class=&quot;barrage-nick&quot;&gt;$&#123;tk_nick&#125;&lt;/span&gt;</span></span><br><span class="line"><span class="addition">+                       &lt;span class=&quot;barrage-city&quot;&gt;$&#123;tk_city&#125;&lt;/span&gt;</span></span><br><span class="line"><span class="addition">+                       &lt;span class=&quot;barrage-time&quot;&gt;$&#123;tk_time&#125;&lt;/span&gt;</span></span><br><span class="line"><span class="addition">+                       &lt;a class=&quot;barrage-close&quot; onclick=&quot;eurkon.switchCommentBarrage()&quot; title=&quot;隐藏热评&quot;&gt;&lt;i class=&quot;fa-solid fa-xmark&quot;&gt;&lt;/i&gt;&lt;/a&gt;</span></span><br><span class="line"><span class="addition">+                     &lt;/div&gt;</span></span><br><span class="line"><span class="addition">+                     &lt;div class=&quot;barrage-content&quot;&gt;</span></span><br><span class="line"><span class="addition">+                       &lt;a title=&quot;跳转至该评论&quot; href=&quot;#$&#123;tk_id&#125;&quot;&gt;$&#123;tk_content&#125;&lt;/a&gt;</span></span><br><span class="line"><span class="addition">+                     &lt;/div&gt;</span></span><br><span class="line"><span class="addition">+                   &lt;/div&gt;</span></span><br><span class="line"><span class="addition">+                 &lt;/div&gt;`</span></span><br><span class="line"><span class="addition">+             &#125;</span></span><br><span class="line"><span class="addition">+             html += &#x27;&lt;/div&gt;&#x27;</span></span><br><span class="line"><span class="addition">+             let barrageContainer = document.getElementById(&#x27;comment-barrage&#x27;) || document.createElement(&#x27;div&#x27;)</span></span><br><span class="line"><span class="addition">+             barrageContainer.id = &#x27;comment-barrage&#x27;</span></span><br><span class="line"><span class="addition">+             barrageContainer.innerHTML = html</span></span><br><span class="line"><span class="addition">+             barrageContainer.style.display = window.localStorage.getItem(&#x27;commentBarrageDisplay&#x27;) === &#x27;false&#x27; ? &#x27;none&#x27; : &#x27;block&#x27;</span></span><br><span class="line"><span class="addition">+             document.getElementById(&#x27;post-comment&#x27;).appendChild(barrageContainer)</span></span><br><span class="line"><span class="addition">+             var barrageSwiper = new Swiper(&#x27;#comment-barrage&#x27;, &#123;</span></span><br><span class="line"><span class="addition">+               direction: &#x27;vertical&#x27;,</span></span><br><span class="line"><span class="addition">+               loop: true,</span></span><br><span class="line"><span class="addition">+               mousewheel: true,</span></span><br><span class="line"><span class="addition">+               autoplay: &#123;</span></span><br><span class="line"><span class="addition">+                 delay: 3000,</span></span><br><span class="line"><span class="addition">+                 disableOnInteraction: true,</span></span><br><span class="line"><span class="addition">+               &#125;</span></span><br><span class="line"><span class="addition">+             &#125;)</span></span><br><span class="line"><span class="addition">+             barrageContainer.onmouseenter = function () &#123;</span></span><br><span class="line"><span class="addition">+               barrageSwiper.autoplay.stop()</span></span><br><span class="line"><span class="addition">+             &#125;;</span></span><br><span class="line"><span class="addition">+             barrageContainer.onmouseleave = function () &#123;</span></span><br><span class="line"><span class="addition">+               barrageSwiper.autoplay.start()</span></span><br><span class="line"><span class="addition">+             &#125;;</span></span><br><span class="line"><span class="addition">+           &#125;</span></span><br><span class="line"><span class="addition">+         &#125;, 1000)</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;, !&#123;JSON.stringify(option)&#125;))</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    const getCount = () =&gt; &#123;</span><br><span class="line">      const countELement = document.getElementById(&#x27;twikoo-count&#x27;)</span><br><span class="line">      if(!countELement) return</span><br><span class="line">      twikoo.getCommentsCount(&#123;</span><br><span class="line">        envId: &#x27;!&#123;envId&#125;&#x27;,</span><br><span class="line">        region: &#x27;!&#123;region&#125;&#x27;,</span><br><span class="line">        urls: [window.location.pathname],</span><br><span class="line">        includeReply: false</span><br><span class="line">      &#125;).then(function (res) &#123;</span><br><span class="line">        countELement.innerText = res[0].count</span><br><span class="line">      &#125;).catch(function (err) &#123;</span><br><span class="line">        console.error(err);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    const runFn = () =&gt; &#123;</span><br><span class="line">      init()</span><br><span class="line">      !&#123;count ? &#x27;GLOBAL_CONFIG_SITE.isPost &amp;&amp; getCount()&#x27; : &#x27;&#x27;&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    const loadTwikoo = () =&gt; &#123;</span><br><span class="line">      if (typeof twikoo <span class="comment">=== &#x27;object&#x27;) &#123;</span></span><br><span class="line">        setTimeout(runFn,0)</span><br><span class="line">        return</span><br><span class="line">      &#125; </span><br><span class="line">      getScript(&#x27;!&#123;url_for(theme.asset.twikoo)&#125;&#x27;).then(runFn)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    if (&#x27;!&#123;use[0]&#125;&#x27; <span class="comment">=== &#x27;Twikoo&#x27; || !!&#123;lazyload&#125;) &#123;</span></span><br><span class="line">      if (!&#123;lazyload&#125;) btf.loadComment(document.getElementById(&#x27;twikoo-wrap&#x27;), loadTwikoo)</span><br><span class="line">      else loadTwikoo()</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">      window.loadOtherComment = () =&gt; &#123;</span><br><span class="line">        loadTwikoo()</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)()</span><br></pre></td></tr></table></figure></p><h2 id="增加自定义样式"><a href="#增加自定义样式" class="headerlink" title="增加自定义样式"></a>增加自定义样式</h2><p>增加自定义样式，下面 css 部分变量需自行修改，按 F12 自取。</p><p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 热评 */</span></span><br><span class="line"><span class="selector-id">#comment-barrage</span> &#123;</span><br><span class="line">  <span class="attribute">display</span>: block;</span><br><span class="line">  <span class="attribute">position</span>: fixed;</span><br><span class="line">  <span class="attribute">z-index</span>: <span class="number">1</span>;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">90%</span>;</span><br><span class="line">  <span class="attribute">bottom</span>: <span class="number">1rem</span>;</span><br><span class="line">  <span class="attribute">right</span>: <span class="number">2rem</span>;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">306px</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line">  <span class="attribute">overflow</span>: hidden;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@media</span> screen <span class="keyword">and</span> (<span class="attribute">max-width:</span> <span class="number">900px</span>) &#123;</span><br><span class="line">  <span class="selector-id">#comment-barrage</span> &#123;</span><br><span class="line">    <span class="attribute">display</span>: none <span class="meta">!important</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#comment-barrage</span> <span class="selector-class">.comment-barrage-item</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">300px</span>;</span><br><span class="line">  <span class="attribute">height</span>: fit-content;</span><br><span class="line">  <span class="attribute">max-height</span>: <span class="number">144px</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">10px</span>;</span><br><span class="line">  <span class="attribute">position</span>: absolute;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">0</span> <span class="number">3px</span>;</span><br><span class="line">  <span class="attribute">bottom</span>: <span class="number">5px</span>;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">var</span>(--card-bg);</span><br><span class="line">  <span class="attribute">border</span>: <span class="built_in">var</span>(--card-border);</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="built_in">var</span>(--border-radius);</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="built_in">var</span>(--box-shadow);</span><br><span class="line">  <span class="attribute">-webkit-transition</span>: all .<span class="number">3s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-moz-transition</span>: all .<span class="number">3s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-o-transition</span>: all .<span class="number">3s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-ms-transition</span>: all .<span class="number">3s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">transition</span>: all .<span class="number">3s</span> ease-in-out;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#comment-barrage</span> <span class="selector-class">.swiper-slide-active</span> <span class="selector-class">.comment-barrage-item</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#comment-barrage</span> <span class="selector-class">.comment-barrage-item</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">border-color</span>: <span class="built_in">var</span>(--main) <span class="meta">!important</span>;</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="built_in">var</span>(--main-shadow) <span class="meta">!important</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-info</span> &#123;</span><br><span class="line">  <span class="attribute">overflow</span>: hidden;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">90%</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">35px</span>;</span><br><span class="line">  <span class="attribute">border-bottom</span>: <span class="built_in">var</span>(--card-border-dashed);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-info</span> <span class="selector-tag">span</span> &#123;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">0</span> .<span class="number">2em</span>;</span><br><span class="line">  <span class="attribute">vertical-align</span>: middle;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-title</span> &#123;</span><br><span class="line">  <span class="attribute">font-weight</span>: bold;</span><br><span class="line">  <span class="attribute">padding</span>: .<span class="number">3em</span> <span class="number">0.5em</span>;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">var</span>(--font-color);</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--card-bg);</span><br><span class="line">  <span class="attribute">margin-right</span>: .<span class="number">5em</span>;</span><br><span class="line">  <span class="attribute">border-radius</span>: .<span class="number">5em</span>;</span><br><span class="line">  <span class="attribute">vertical-align</span>: middle;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-title</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--second);</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">var</span>(--main);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-avatar</span> &#123;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">2em</span>;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">2em</span>;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span><br><span class="line">  <span class="attribute">vertical-align</span>: middle;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-nick</span> &#123;</span><br><span class="line">  <span class="attribute">font-weight</span>: bold;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-close</span> &#123;</span><br><span class="line">  <span class="attribute">position</span>: absolute;</span><br><span class="line">  <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">right</span>: <span class="number">10px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-content</span> &#123;</span><br><span class="line">  <span class="attribute">padding-top</span>: <span class="number">5px</span>;</span><br><span class="line">  <span class="attribute">max-height</span>: <span class="number">95px</span>;</span><br><span class="line">  <span class="attribute">height</span>: fit-content;</span><br><span class="line">  <span class="attribute">overflow-y</span>: scroll;</span><br><span class="line">  <span class="attribute">text-align</span>: justify;</span><br><span class="line">  <span class="attribute">word-break</span>: break-all;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-content</span> <span class="selector-tag">a</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--font-color);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-content</span> <span class="selector-tag">a</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--theme-color);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-content</span> <span class="selector-tag">p</span> &#123;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.barrage-content</span> <span class="selector-tag">img</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">3em</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="增加-隐藏-显示-热评-js"><a href="#增加-隐藏-显示-热评-js" class="headerlink" title="增加 隐藏/显示 热评 js"></a>增加 隐藏/显示 热评 js</h2><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 切换热评</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">switchCommentBarrage</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> flag = <span class="built_in">window</span>.localStorage.getItem(<span class="string">&#x27;commentBarrageDisplay&#x27;</span>) <span class="comment">// undefined || false</span></span><br><span class="line">  <span class="built_in">document</span>.getElementById(<span class="string">&#x27;comment-barrage&#x27;</span>).style.display = flag === <span class="string">&#x27;false&#x27;</span> ? <span class="string">&#x27;block&#x27;</span> : <span class="string">&#x27;none&#x27;</span></span><br><span class="line">  <span class="comment">// 本地缓存一天，刷新或切换页面时仍 隐藏或显示 热评。</span></span><br><span class="line">  <span class="built_in">window</span>.localStorage.setItem(<span class="string">&#x27;commentBarrageDisplay&#x27;</span>, flag === <span class="string">&#x27;false&#x27;</span> ? <span class="string">&#x27;undefined&#x27;</span> : <span class="string">&#x27;false&#x27;</span>, <span class="number">86400000</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="引入-swiper-轮播功能"><a href="#引入-swiper-轮播功能" class="headerlink" title="引入 swiper 轮播功能"></a>引入 swiper 轮播功能</h2><p>修改 <code>_config.butterfly.yml</code> 文件</p><p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Inject</span></span><br><span class="line"><span class="comment"># Insert the code to head (before &#x27;&lt;/head&gt;&#x27; tag) and the bottom (before &#x27;&lt;/body&gt;&#x27; tag)</span></span><br><span class="line"><span class="comment"># 插入代码到头部 &lt;/head&gt; 之前 和 底部 &lt;/body&gt; 之前</span></span><br><span class="line"><span class="attr">inject:</span></span><br><span class="line">  <span class="attr">head:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&lt;link</span> <span class="string">rel=&quot;stylesheet&quot;</span> <span class="string">href=&quot;https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/Swiper/8.0.6/swiper-bundle.min.css&quot;&gt;</span> <span class="comment"># 轮播</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&lt;script</span> <span class="string">data-pjax</span> <span class="string">src=&quot;https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/Swiper/8.0.6/swiper-bundle.min.js&quot;&gt;&lt;/script&gt;</span> <span class="comment"># 轮播</span></span><br></pre></td></tr></table></figure></p><h2 id="Hexo-三连"><a href="#Hexo-三连" class="headerlink" title="Hexo 三连"></a>Hexo 三连</h2><p>执行 Hexo 三连</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo g &amp;&amp; hexo s</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.eurkon.com/tags/Butterfly/">Butterfly</category>
      
      
      <comments>https://blog.eurkon.com/post/364efc10.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Butterfly 推荐文章增加文章描述</title>
      <link>https://blog.eurkon.com/post/3d2664bb.html</link>
      <guid>https://blog.eurkon.com/post/3d2664bb.html</guid>
      <pubDate>Tue, 16 Aug 2022 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文用于 butterfly 魔改，博主没有测试是否适配于其他主题，以及自定义样式 CSS 可能需要一定的前端知识进行优化。
&lt;code&gt;_</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文用于 butterfly 魔改，博主没有测试是否适配于其他主题，以及自定义样式 CSS 可能需要一定的前端知识进行优化。<code>_config.butterfly.yml</code> 配置文件可修改显示 文字描述（文章 <code>description</code> 属性）还是 文章内容（默认截取 500 字），以及推荐文章数量。</p><p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Related Articles</span></span><br><span class="line"><span class="attr">related_post:</span></span><br><span class="line">  <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">limit:</span> <span class="number">6</span> <span class="comment"># Number of posts displayed</span></span><br><span class="line">  <span class="attr">date_type:</span> <span class="string">created</span> <span class="comment"># or created or updated 文章日期顯示創建日或者更新日</span></span><br></pre></td></tr></table></figure></p><p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Display the article introduction on homepage</span></span><br><span class="line"><span class="comment"># 1: description</span></span><br><span class="line"><span class="comment"># 2: both (if the description exists, it will show description, or show the auto_excerpt)</span></span><br><span class="line"><span class="comment"># 3: auto_excerpt (default)</span></span><br><span class="line"><span class="comment"># false: do not show the article introduction</span></span><br><span class="line"><span class="attr">index_post_content:</span></span><br><span class="line">  <span class="attr">method:</span> <span class="number">2</span></span><br><span class="line">  <span class="attr">length:</span> <span class="number">500</span> <span class="comment"># if you set method to 2 or 3, the length need to config</span></span><br></pre></td></tr></table></figure></p><h2 id="效果预览"><a href="#效果预览" class="headerlink" title="效果预览"></a>效果预览</h2><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/cover/butterfly_related_post.png" alt="推荐文章增加描文章描述"></p><h2 id="推荐文章增加描文章描述"><a href="#推荐文章增加描文章描述" class="headerlink" title="推荐文章增加描文章描述"></a>推荐文章增加描文章描述</h2><p><code>[Blogroot]\themes\butterfly\scripts\helpers\related_post.js</code> 增加以下内容。</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * Butterfly</span><br><span class="line"> * Related Posts</span><br><span class="line"> * According the tag</span><br><span class="line"> */</span><br><span class="line"></span><br><span class="line">&#x27;use strict&#x27;</span><br><span class="line"></span><br><span class="line">hexo.extend.helper.register(&#x27;related_posts&#x27;, function (currentPost, allPosts) &#123;</span><br><span class="line">  let relatedPosts = []</span><br><span class="line">  currentPost.tags.forEach(function (tag) &#123;</span><br><span class="line">    allPosts.forEach(function (post) &#123;</span><br><span class="line">      if (isTagRelated(tag.name, post.tags)) &#123;</span><br><span class="line">        const relatedPost = &#123;</span><br><span class="line">          title: post.title,</span><br><span class="line">          path: post.path,</span><br><span class="line">          cover: post.cover,</span><br><span class="line">          randomcover: post.randomcover,</span><br><span class="line">          weight: 1,</span><br><span class="line"><span class="addition">+         description: post.description,</span></span><br><span class="line"><span class="addition">+         content: post.content,</span></span><br><span class="line">          updated: post.updated,</span><br><span class="line">          created: post.date</span><br><span class="line">        &#125;</span><br><span class="line">        const index = findItem(relatedPosts, &#x27;path&#x27;, post.path)</span><br><span class="line">        if (index !== -1) &#123;</span><br><span class="line">          relatedPosts[index].weight += 1</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">          if (currentPost.path !== post.path) &#123;</span><br><span class="line">            relatedPosts.push(relatedPost)</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">  if (relatedPosts.length <span class="comment">=== 0) &#123;</span></span><br><span class="line">    return &#x27;&#x27;</span><br><span class="line">  &#125;</span><br><span class="line">  let result = &#x27;&#x27;</span><br><span class="line">  const hexoConfig = hexo.config</span><br><span class="line">  const config = hexo.theme.config</span><br><span class="line"></span><br><span class="line">  const limitNum = config.related_post.limit || 6</span><br><span class="line">  const dateType = config.related_post.date_type || &#x27;created&#x27;</span><br><span class="line">  const headlineLang = this._p(&#x27;post.recommend&#x27;)</span><br><span class="line"></span><br><span class="line">  relatedPosts = relatedPosts.sort(compare(&#x27;weight&#x27;))</span><br><span class="line"></span><br><span class="line">  if (relatedPosts.length &gt; 0) &#123;</span><br><span class="line">    result += &#x27;&lt;div class=&quot;relatedPosts&quot;&gt;&#x27;</span><br><span class="line">    result += `&lt;div class=&quot;headline&quot;&gt;&lt;i class=&quot;fas fa-thumbs-up fa-fw&quot;&gt;&lt;/i&gt;&lt;span&gt;$&#123;headlineLang&#125;&lt;/span&gt;&lt;/div&gt;`</span><br><span class="line">    result += &#x27;&lt;div class=&quot;relatedPosts-list&quot;&gt;&#x27;</span><br><span class="line"> </span><br><span class="line">    for (let i = 0; i &lt; Math.min(relatedPosts.length, limitNum); i++) &#123;</span><br><span class="line">      const cover =</span><br><span class="line">        relatedPosts[i].cover <span class="comment">=== false</span></span><br><span class="line">          ? relatedPosts[i].randomcover</span><br><span class="line">          : relatedPosts[i].cover</span><br><span class="line">      const title = this.escape_html(relatedPosts[i].title)</span><br><span class="line"><span class="addition">+     const description = this.strip_html(relatedPosts[i].description)</span></span><br><span class="line"><span class="addition">+     const content = this.strip_html(relatedPosts[i].content)</span></span><br><span class="line">      result += `&lt;div&gt;&lt;a href=&quot;$&#123;this.url_for(relatedPosts[i].path)&#125;&quot; title=&quot;$&#123;title&#125;&quot;&gt;`</span><br><span class="line">      result += `&lt;img class=&quot;cover&quot; src=&quot;$&#123;this.url_for(cover)&#125;&quot; alt=&quot;cover&quot;&gt;`</span><br><span class="line">      if (dateType <span class="comment">=== &#x27;created&#x27;) &#123;</span></span><br><span class="line">        result += `&lt;div class=&quot;content is-center&quot;&gt;&lt;div class=&quot;date&quot;&gt;&lt;i class=&quot;far fa-calendar-alt fa-fw&quot;&gt;&lt;/i&gt; $&#123;this.date(relatedPosts[i].created, hexoConfig.date_format)&#125;&lt;/div&gt;`</span><br><span class="line">      &#125; else &#123;</span><br><span class="line">        result += `&lt;div class=&quot;content is-center&quot;&gt;&lt;div class=&quot;date&quot;&gt;&lt;i class=&quot;fas fa-history fa-fw&quot;&gt;&lt;/i&gt; $&#123;this.date(relatedPosts[i].updated, hexoConfig.date_format)&#125;&lt;/div&gt;`</span><br><span class="line">      &#125;</span><br><span class="line">      result += `&lt;div class=&quot;title&quot;&gt;$&#123;title&#125;&lt;/div&gt;`</span><br><span class="line"><span class="addition">+     switch (config.index_post_content.method) &#123;</span></span><br><span class="line"><span class="addition">+       case false:</span></span><br><span class="line"><span class="addition">+         break</span></span><br><span class="line"><span class="addition">+       case 1:</span></span><br><span class="line"><span class="addition">+         result += `&lt;div class=&quot;info&quot;&gt;$&#123;description&#125;&lt;/div&gt;`</span></span><br><span class="line"><span class="addition">+         break</span></span><br><span class="line"><span class="addition">+       case 2:</span></span><br><span class="line"><span class="addition">+         if (description) &#123;</span></span><br><span class="line"><span class="addition">+           result += `&lt;div class=&quot;info&quot;&gt;$&#123;description&#125;&lt;/div&gt;`</span></span><br><span class="line"><span class="addition">+         &#125;</span></span><br><span class="line"><span class="addition">+         else &#123;</span></span><br><span class="line"><span class="addition">+           let expert = content.substring(0, config.index_post_content.length)</span></span><br><span class="line"><span class="addition">+           content.length &gt; config.index_post_content.length ? expert += &#x27; ...&#x27; : &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+           result += `&lt;div class=&quot;info&quot;&gt;$&#123;expert&#125;&lt;/div&gt;`</span></span><br><span class="line"><span class="addition">+         &#125;</span></span><br><span class="line"><span class="addition">+         break</span></span><br><span class="line"><span class="addition">+       default:</span></span><br><span class="line"><span class="addition">+         let expert = content.substring(0, config.index_post_content.length)</span></span><br><span class="line"><span class="addition">+         content.length &gt; config.index_post_content.length ? expert += &#x27; ...&#x27; : &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+         result += `&lt;div class=&quot;info&quot;&gt;$&#123;expert&#125;&lt;/div&gt;`</span></span><br><span class="line"><span class="addition">+         break</span></span><br><span class="line"><span class="addition">+     &#125;</span></span><br><span class="line">      result += &#x27;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&#x27;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    result += &#x27;&lt;/div&gt;&lt;/div&gt;&#x27;</span><br><span class="line">    return result</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br><span class="line"> </span><br><span class="line">function isTagRelated (tagName, TBDtags) &#123;</span><br><span class="line">  let result = false</span><br><span class="line">  TBDtags.forEach(function (tag) &#123;</span><br><span class="line">    if (tagName <span class="comment">=== tag.name) &#123;</span></span><br><span class="line">      result = true</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">  return result</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function findItem (arrayToSearch, attr, val) &#123;</span><br><span class="line">  for (let i = 0; i &lt; arrayToSearch.length; i++) &#123;</span><br><span class="line">    if (arrayToSearch[i][attr] <span class="comment">=== val) &#123;</span></span><br><span class="line">      return i</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  return -1</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function compare (attr) &#123;</span><br><span class="line">  return function (a, b) &#123;</span><br><span class="line">    const val1 = a[attr]</span><br><span class="line">    const val2 = b[attr]</span><br><span class="line">    return val2 - val1</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="上下一篇文章增加文章描述"><a href="#上下一篇文章增加文章描述" class="headerlink" title="上下一篇文章增加文章描述"></a>上下一篇文章增加文章描述</h2><p><code>[Blogroot]\themes\butterfly\layout\includes\pagination.pug</code> 增加以下内容。<figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">-</span></span><br><span class="line">  var options = &#123;</span><br><span class="line">    prev_text: &#x27;&lt;i class=&quot;fas fa-chevron-left fa-fw&quot;&gt;&lt;/i&gt;&#x27;,</span><br><span class="line">    next_text: &#x27;&lt;i class=&quot;fas fa-chevron-right fa-fw&quot;&gt;&lt;/i&gt;&#x27;,</span><br><span class="line">    mid_size: 1,</span><br><span class="line">    escape: false</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">if is_post()</span><br><span class="line">  - let prev = theme.post_pagination <span class="comment">=== 1 ? page.prev : page.next</span></span><br><span class="line">  - let next = theme.post_pagination <span class="comment">=== 1 ? page.next : page.prev</span></span><br><span class="line">  nav#pagination.pagination-post</span><br><span class="line">    if(prev)</span><br><span class="line">      - var hasPageNext = next ? &#x27;pull-left&#x27; : &#x27;pull-full&#x27;</span><br><span class="line">      .prev-post(class=hasPageNext)</span><br><span class="line">        - var pagination_cover = prev.cover <span class="comment">=== false ? prev.randomcover : prev.cover</span></span><br><span class="line">        a(href=url_for(prev.path))</span><br><span class="line">          img.prev-cover(src=url_for(pagination_cover) onerror=`onerror=null;src=&#x27;$&#123;url_for(theme.error_img.post_page)&#125;&#x27;` alt=&#x27;cover of previous post&#x27;)</span><br><span class="line">          .pagination-info</span><br><span class="line">            .label=_p(&#x27;pagination.prev&#x27;)</span><br><span class="line">            .prev_info=prev.title</span><br><span class="line"><span class="addition">+           case theme.index_post_content.method</span></span><br><span class="line"><span class="addition">+             when false</span></span><br><span class="line"><span class="addition">+               - break</span></span><br><span class="line"><span class="addition">+             when 1</span></span><br><span class="line"><span class="addition">+               .content!= prev.description</span></span><br><span class="line"><span class="addition">+             when 2</span></span><br><span class="line"><span class="addition">+               if prev.description</span></span><br><span class="line"><span class="addition">+                 .content!= prev.description</span></span><br><span class="line"><span class="addition">+               else</span></span><br><span class="line"><span class="addition">+                 - const content = strip_html(prev.content)</span></span><br><span class="line"><span class="addition">+                 - let expert = content.substring(0, theme.index_post_content.length) </span></span><br><span class="line"><span class="addition">+                 - content.length &gt; theme.index_post_content.length ? expert += &#x27; ...&#x27; : &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+                 .content!= expert</span></span><br><span class="line"><span class="addition">+             default</span></span><br><span class="line"><span class="addition">+               - const content = strip_html(prev.content)</span></span><br><span class="line"><span class="addition">+               - let expert = content.substring(0, theme.index_post_content.length) </span></span><br><span class="line"><span class="addition">+               - content.length &gt; theme.index_post_content.length ? expert += &#x27; ...&#x27; : &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+               .content!= expert</span></span><br><span class="line">          </span><br><span class="line">    if(next)</span><br><span class="line">      - var hasPagePrev = prev ? &#x27;pull-right&#x27; : &#x27;pull-full&#x27;</span><br><span class="line">      - var pagination_cover = next.cover == false ? next.randomcover : next.cover</span><br><span class="line">      .next-post(class=hasPagePrev)</span><br><span class="line">        a(href=url_for(next.path))</span><br><span class="line">          img.next-cover(src=url_for(pagination_cover) onerror=`onerror=null;src=&#x27;$&#123;url_for(theme.error_img.post_page)&#125;&#x27;` alt=&#x27;cover of next post&#x27;)</span><br><span class="line">          .pagination-info</span><br><span class="line">            .label=_p(&#x27;pagination.next&#x27;)</span><br><span class="line">            .next_info=next.title</span><br><span class="line"><span class="addition">+           case theme.index_post_content.method</span></span><br><span class="line"><span class="addition">+             when false</span></span><br><span class="line"><span class="addition">+               - break</span></span><br><span class="line"><span class="addition">+             when 1</span></span><br><span class="line"><span class="addition">+               .content!= next.description</span></span><br><span class="line"><span class="addition">+             when 2</span></span><br><span class="line"><span class="addition">+               if next.description</span></span><br><span class="line"><span class="addition">+                 .content!= next.description</span></span><br><span class="line"><span class="addition">+               else</span></span><br><span class="line"><span class="addition">+                 - const content = strip_html(next.content)</span></span><br><span class="line"><span class="addition">+                 - let expert = content.substring(0, theme.index_post_content.length) </span></span><br><span class="line"><span class="addition">+                 - content.length &gt; theme.index_post_content.length ? expert += &#x27; ...&#x27; : &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+                 .content!= expert</span></span><br><span class="line"><span class="addition">+             default</span></span><br><span class="line"><span class="addition">+               - const content = strip_html(next.content)</span></span><br><span class="line"><span class="addition">+               - let expert = content.substring(0, theme.index_post_content.length) </span></span><br><span class="line"><span class="addition">+               - content.length &gt; theme.index_post_content.length ? expert += &#x27; ...&#x27; : &#x27;&#x27;</span></span><br><span class="line"><span class="addition">+               .content!= expert</span></span><br><span class="line">else</span><br><span class="line">  nav#pagination</span><br><span class="line">    .pagination</span><br><span class="line">      if is_home()</span><br><span class="line">        - options.format = &#x27;page/%d/#content-inner&#x27;</span><br><span class="line">      !=paginator(options)</span><br></pre></td></tr></table></figure></p><h2 id="增加自定义样式"><a href="#增加自定义样式" class="headerlink" title="增加自定义样式"></a>增加自定义样式</h2><p>增加自定义样式，其中部分是博主魔改后的样式，仅供参考。</p><p>如 <code>--main: #1677B3</code>、<code>--second: #fff</code>、<code>--card-border: 1px solid rgba(150,150,150,0.2);</code>、<code>--border-radius: .5rem</code>、<code>--main-shadow: 0 2px 3px 1px rgba(22, 119, 179, .2)</code>。</p><p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 推荐文章 */</span></span><br><span class="line"><span class="selector-class">.relatedPosts</span>&gt;<span class="selector-class">.relatedPosts-list</span> <span class="selector-class">.content</span> &#123;</span><br><span class="line">  <span class="attribute">position</span>: relative;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.relatedPosts</span>&gt;<span class="selector-class">.relatedPosts-list</span>&gt;<span class="selector-tag">div</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="built_in">var</span>(--border-radius);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.relatedPosts</span>&gt;<span class="selector-class">.relatedPosts-list</span> <span class="selector-class">.content</span> <span class="selector-class">.date</span> &#123;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">1rem</span>;</span><br><span class="line">  <span class="attribute">-webkit-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-moz-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-o-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-ms-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.relatedPosts</span>&gt;<span class="selector-class">.relatedPosts-list</span> <span class="selector-class">.content</span> <span class="selector-class">.title</span> &#123;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">1.1rem</span>;</span><br><span class="line">  <span class="attribute">-webkit-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-moz-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-o-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-ms-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.relatedPosts</span>&gt;<span class="selector-class">.relatedPosts-list</span>&gt;<span class="selector-tag">div</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.cover</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: .<span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.relatedPosts</span>&gt;<span class="selector-class">.relatedPosts-list</span>&gt;<span class="selector-tag">div</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">var</span>(--main);</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--second);</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="built_in">var</span>(--main-shadow);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.relatedPosts</span>&gt;<span class="selector-class">.relatedPosts-list</span>&gt;<span class="selector-tag">div</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.content</span> <span class="selector-class">.date</span>,</span><br><span class="line"><span class="selector-class">.relatedPosts</span>&gt;<span class="selector-class">.relatedPosts-list</span>&gt;<span class="selector-tag">div</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.content</span> <span class="selector-class">.title</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.relatedPosts</span>&gt;<span class="selector-class">.relatedPosts-list</span> <span class="selector-class">.content</span> <span class="selector-class">.info</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">position</span>: absolute;</span><br><span class="line">  <span class="attribute">top</span>: <span class="number">50%</span>;</span><br><span class="line">  <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">right</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">1rem</span>;</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--second);</span><br><span class="line">  <span class="attribute">text-align</span>: justify;</span><br><span class="line">  <span class="attribute">word-break</span>: break-all;</span><br><span class="line">  <span class="attribute">-webkit-line-clamp</span>: <span class="number">3</span>;</span><br><span class="line">  <span class="attribute">display</span>: -webkit-box;</span><br><span class="line">  <span class="attribute">overflow</span>: hidden;</span><br><span class="line">  <span class="attribute">-webkit-box-orient</span>: vertical;</span><br><span class="line">  <span class="attribute">-webkit-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-moz-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-o-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-ms-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-webkit-transform</span>: <span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br><span class="line">  <span class="attribute">-moz-transform</span>: <span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br><span class="line">  <span class="attribute">-o-transform</span>: <span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br><span class="line">  <span class="attribute">-ms-transform</span>: <span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.relatedPosts</span>&gt;<span class="selector-class">.relatedPosts-list</span>&gt;<span class="selector-tag">div</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.content</span> <span class="selector-class">.info</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 上下一篇文章 */</span></span><br><span class="line"><span class="selector-id">#pagination</span><span class="selector-class">.pagination-post</span> &#123;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="built_in">var</span>(--border-radius);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.prev-post</span> <span class="selector-class">.label</span>,</span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.next-post</span> <span class="selector-class">.label</span> &#123;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">1rem</span>;</span><br><span class="line">  <span class="attribute">-webkit-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-moz-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-o-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-ms-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.prev-post</span> <span class="selector-class">.prev_info</span>,</span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.next-post</span> <span class="selector-class">.next_info</span> &#123;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">1.1rem</span>;</span><br><span class="line">  <span class="attribute">-webkit-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-moz-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-o-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-ms-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.prev-post</span>,</span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.next-post</span> &#123;</span><br><span class="line">  <span class="attribute">border</span>: <span class="built_in">var</span>(--card-border);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.prev-post</span><span class="selector-pseudo">:hover</span> <span class="selector-tag">img</span>,</span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.next-post</span><span class="selector-pseudo">:hover</span> <span class="selector-tag">img</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: .<span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.prev-post</span><span class="selector-pseudo">:hover</span> <span class="selector-tag">a</span>,</span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.next-post</span><span class="selector-pseudo">:hover</span> <span class="selector-tag">a</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">var</span>(--main);</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--second);</span><br><span class="line">  <span class="attribute">box-shadow</span>: <span class="built_in">var</span>(--main-shadow);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.prev-post</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.label</span>,</span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.next-post</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.label</span>,</span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.prev-post</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.prev_info</span>,</span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.next-post</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.next_info</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.content</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">position</span>: absolute;</span><br><span class="line">  <span class="attribute">top</span>: <span class="number">50%</span>;</span><br><span class="line">  <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">right</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">0</span> <span class="number">1rem</span>;</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--second);</span><br><span class="line">  <span class="attribute">text-align</span>: justify;</span><br><span class="line">  <span class="attribute">word-break</span>: break-all;</span><br><span class="line">  <span class="attribute">-webkit-line-clamp</span>: <span class="number">3</span>;</span><br><span class="line">  <span class="attribute">display</span>: -webkit-box;</span><br><span class="line">  <span class="attribute">overflow</span>: hidden;</span><br><span class="line">  <span class="attribute">-webkit-box-orient</span>: vertical;</span><br><span class="line">  <span class="attribute">-webkit-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-moz-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-o-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-ms-transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">transition</span>: all .<span class="number">6s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-webkit-transform</span>: <span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br><span class="line">  <span class="attribute">-moz-transform</span>: <span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br><span class="line">  <span class="attribute">-o-transform</span>: <span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br><span class="line">  <span class="attribute">-ms-transform</span>: <span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.prev-post</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.content</span>,</span><br><span class="line"><span class="selector-id">#pagination</span> <span class="selector-class">.next-post</span><span class="selector-pseudo">:hover</span> <span class="selector-class">.content</span> &#123;</span><br><span class="line">  <span class="attribute">opacity</span>: <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="Hexo-三连"><a href="#Hexo-三连" class="headerlink" title="Hexo 三连"></a>Hexo 三连</h2><p>执行 Hexo 三连</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo g &amp;&amp; hexo s</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.eurkon.com/tags/Butterfly/">Butterfly</category>
      
      
      <comments>https://blog.eurkon.com/post/3d2664bb.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Butterfly 分类标签归档页增加文章索引</title>
      <link>https://blog.eurkon.com/post/27df86b.html</link>
      <guid>https://blog.eurkon.com/post/27df86b.html</guid>
      <pubDate>Thu, 28 Jul 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文教程主要针对 Hexo Butterfly 主题博客，基于原版主题增加文章索引可能不够美观，只是分享思路，有兴趣和有基础的小伙伴可以对此</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文教程主要针对 Hexo Butterfly 主题博客，基于原版主题增加文章索引可能不够美观，只是分享思路，有兴趣和有基础的小伙伴可以对此二次魔改。</p><blockquote><p><strong>开发思路</strong>：参考 <a href="https://hexo.io/zh-cn/docs/variables#%E9%A1%B5%E9%9D%A2%E5%8F%98%E9%87%8F">Hexo 页面变量</a> 中的 <code>page.current</code> 获取当前页码，以及 Hexo 配置文件中 <code>[Blogroot]\_config.yml</code> 的 <code>per_page</code> 每页显示的文章量计算文章索引。</p></blockquote><h2 id="修改文章渲染函数"><a href="#修改文章渲染函数" class="headerlink" title="修改文章渲染函数"></a>修改文章渲染函数</h2><p>打开 <code>\themes\butterfly\layout\includes\mixins\article-sort.pug</code> 文件。</p><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab active">butterfly4.2.2</button><button type="button" class="tab">本站魔改</button></div><div class="tab-contents"><div class="tab-item-content active"><p>基于原版主题，增加文章索引可能不够美观，只是分享思路。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/cover/butterfly_post_index_butterfly.png" alt="butterfly4.2.2 版本"></p><p>修改以下代码</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">- mixin articleSort(posts)</span></span><br><span class="line"><span class="addition">+ mixin articleSort(posts, current)</span></span><br><span class="line">    .article-sort</span><br><span class="line">      - var year</span><br><span class="line">      - posts.each(function (article) &#123;</span><br><span class="line">        - let tempYear = date(article.date, &#x27;YYYY&#x27;)</span><br><span class="line">        - let no_cover = article.cover <span class="comment">=== false || !theme.cover.archives_enable ? &#x27;no-article-cover&#x27; : &#x27;&#x27;</span></span><br><span class="line">        - let title = article.title || _p(&#x27;no_title&#x27;)</span><br><span class="line">        if tempYear !== year</span><br><span class="line">          - year = tempYear</span><br><span class="line">          .article-sort-item.year= year</span><br><span class="line">        .article-sort-item(class=no_cover)</span><br><span class="line">          if article.cover &amp;&amp; theme.cover.archives_enable</span><br><span class="line">            a.article-sort-item-img(href=url_for(article.path) title=title)</span><br><span class="line">              img(src=url_for(article.cover) alt=title onerror=`this.onerror=null;this.src=&#x27;$&#123;url_for(theme.error_img.post_page)&#125;&#x27;`)</span><br><span class="line">          .article-sort-item-info</span><br><span class="line">            .article-sort-item-time</span><br><span class="line">              i.far.fa-calendar-alt</span><br><span class="line">              time.post-meta-date-created(datetime=date_xml(article.date) title=_p(&#x27;post.created&#x27;) + &#x27; &#x27; + full_date(article.date))= date(article.date, config.date_format)</span><br><span class="line">            a.article-sort-item-title(href=url_for(article.path) title=title)= title</span><br><span class="line"><span class="addition">+           span.article-sort-item-index= (current - 1) * config.per_page + post_index + 1</span></span><br><span class="line">      - &#125;)</span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content"><p>需配合本站 CSS 样式魔改，有一定的前端基础。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/cover/butterfly_post_index_eurkon.png" alt="博主魔改版本"></p><p>替换为以下代码</p><p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">mixin articleSort(posts, current)</span><br><span class="line">  .article-sort</span><br><span class="line">    - var year</span><br><span class="line">    - posts.each(function (article, post_index) &#123;</span><br><span class="line">      - let tempYear &#x3D; date(article.date, &#39;YYYY&#39;)</span><br><span class="line">      - let no_cover &#x3D; article.cover &#x3D;&#x3D;&#x3D; false || !theme.cover.archives_enable ? &#39;no-article-cover&#39; : &#39;&#39;</span><br><span class="line">      - let title &#x3D; article.title || _p(&#39;no_title&#39;)</span><br><span class="line">      if tempYear !&#x3D;&#x3D; year</span><br><span class="line">        - year &#x3D; tempYear</span><br><span class="line">        .article-sort-item.year&#x3D; year</span><br><span class="line">      .article-sort-item(class&#x3D;no_cover)</span><br><span class="line">        if article.cover &amp;&amp; theme.cover.archives_enable</span><br><span class="line">          a.article-sort-item-img(href&#x3D;url_for(article.path) title&#x3D;title)</span><br><span class="line">            img(src&#x3D;url_for(article.cover) alt&#x3D;title onerror&#x3D;&#96;this.onerror&#x3D;null;this.src&#x3D;&#39;$&#123;url_for(theme.error_img.post_page)&#125;&#39;&#96;)</span><br><span class="line">        .article-sort-item-info</span><br><span class="line">          a.article-sort-item-title(href&#x3D;url_for(article.path) title&#x3D;title)&#x3D; title</span><br><span class="line">          span.article-sort-item-index&#x3D; (current - 1) * config.per_page + post_index + 1</span><br><span class="line">          .article-meta-wrap</span><br><span class="line">            if (theme.post_meta.page.categories &amp;&amp; article.categories.data.length &gt; 0)</span><br><span class="line">              span.article-sort-item-categories</span><br><span class="line">                i.fas.fa-inbox</span><br><span class="line">                each item, index in article.categories.data</span><br><span class="line">                  a(href&#x3D;url_for(item.path)).article-meta__categories #[&#x3D;item.name]</span><br><span class="line">                  if (index &lt; article.categories.data.length - 1)</span><br><span class="line">                    i.fas.fa-angle-right</span><br><span class="line">            if (theme.post_meta.page.tags &amp;&amp; article.tags.data.length &gt; 0)</span><br><span class="line">              span.article-sort-item-tags</span><br><span class="line">                i.fas.fa-tag</span><br><span class="line">                each item, index in article.tags.data</span><br><span class="line">                  a(href&#x3D;url_for(item.path)).article-meta__tags #[&#x3D;item.name]</span><br><span class="line">                  if (index &lt; article.tags.data.length - 1)</span><br><span class="line">                    span.article-meta__link #[&#x3D;&#39;•&#39;]</span><br><span class="line">            .article-sort-item-time</span><br><span class="line">              i.far.fa-calendar-alt</span><br><span class="line">              time.post-meta-date-created(datetime&#x3D;date_xml(article.date) title&#x3D;_p(&#39;post.created&#39;) + &#39; &#39; + full_date(article.date))&#x3D; date(article.date, config.date_format)</span><br><span class="line">    - &#125;)</span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><h2 id="修改归档、分类、标签页面"><a href="#修改归档、分类、标签页面" class="headerlink" title="修改归档、分类、标签页面"></a>修改归档、分类、标签页面</h2><h3 id="归档页面"><a href="#归档页面" class="headerlink" title="归档页面"></a>归档页面</h3><p><code>\themes\butterfly\layout\archive.pug</code></p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">extends includes/layout.pug</span><br><span class="line"></span><br><span class="line">block content</span><br><span class="line">  include ./includes/mixins/article-sort.pug</span><br><span class="line">  #archive</span><br><span class="line">    .article-sort-title= _p(&#x27;page.articles&#x27;) + &#x27; - &#x27; + site.posts.length</span><br><span class="line"><span class="deletion">-   +articleSort(page.posts)</span></span><br><span class="line"><span class="addition">+   +articleSort(page.posts, page.current)</span></span><br><span class="line">    include includes/pagination.pug</span><br></pre></td></tr></table></figure></p><h3 id="分类页面"><a href="#分类页面" class="headerlink" title="分类页面"></a>分类页面</h3><p><code>\themes\butterfly\layout\category.pug</code></p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">extends includes/layout.pug</span><br><span class="line"></span><br><span class="line">block content</span><br><span class="line">  if theme.category_ui == &#x27;index&#x27;</span><br><span class="line">    include ./includes/mixins/post-ui.pug</span><br><span class="line">    #recent-posts.recent-posts.category_ui   </span><br><span class="line">      +postUI</span><br><span class="line">      include includes/pagination.pug    </span><br><span class="line">  else</span><br><span class="line">    include ./includes/mixins/article-sort.pug</span><br><span class="line">    #category</span><br><span class="line">      .article-sort-title= _p(&#x27;page.category&#x27;) + &#x27; - &#x27; + page.category</span><br><span class="line"><span class="deletion">-     +articleSort(page.posts)</span></span><br><span class="line"><span class="addition">+     +articleSort(page.posts, page.current)</span></span><br><span class="line">      include includes/pagination.pug</span><br></pre></td></tr></table></figure></p><h3 id="标签页面"><a href="#标签页面" class="headerlink" title="标签页面"></a>标签页面</h3><p><code>\themes\butterfly\layout\tag.pug</code></p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">extends includes/layout.pug</span><br><span class="line"></span><br><span class="line">block content</span><br><span class="line">  if theme.tag_ui == &#x27;index&#x27;</span><br><span class="line">    include ./includes/mixins/post-ui.pug</span><br><span class="line">    #recent-posts.recent-posts</span><br><span class="line">      +postUI</span><br><span class="line">      include includes/pagination.pug</span><br><span class="line">  else</span><br><span class="line">    include ./includes/mixins/article-sort.pug</span><br><span class="line">    #tag</span><br><span class="line">      .article-sort-title= _p(&#x27;page.tag&#x27;) + &#x27; - &#x27; + page.tag</span><br><span class="line"><span class="deletion">-     +articleSort(page.posts)</span></span><br><span class="line"><span class="addition">+     +articleSort(page.posts, page.current)</span></span><br><span class="line">      include includes/pagination.pug</span><br></pre></td></tr></table></figure></p><h2 id="增加文章索引样式"><a href="#增加文章索引样式" class="headerlink" title="增加文章索引样式"></a>增加文章索引样式</h2><p><code>\themes\butterfly\source\css\_page\archives.styl</code>，大概在 100 行的位置，增加 <code>.article-sort-item-index</code> 的样式.</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br></pre></td><td class="code"><pre><span class="line">.article-sort</span><br><span class="line">  margin-left: 10px</span><br><span class="line">  padding-left: 20px</span><br><span class="line">  border-left: 2px solid lighten($light-blue, 20)</span><br><span class="line"></span><br><span class="line">  &amp;-title</span><br><span class="line">    position: relative</span><br><span class="line">    margin-left: 10px</span><br><span class="line">    padding-bottom: 20px</span><br><span class="line">    padding-left: 20px</span><br><span class="line">    font-size: 1.72em</span><br><span class="line"></span><br><span class="line">    &amp;:hover</span><br><span class="line">      &amp;:before</span><br><span class="line">        border-color: var(--pseudo-hover)</span><br><span class="line"></span><br><span class="line">    &amp;:before</span><br><span class="line">      position: absolute</span><br><span class="line">      top: calc(((100% - 36px) / 2))</span><br><span class="line">      left: -9px</span><br><span class="line">      z-index: 1</span><br><span class="line">      width: w = 10px</span><br><span class="line">      height: h = w</span><br><span class="line">      border: .5 * w solid $light-blue</span><br><span class="line">      border-radius: w</span><br><span class="line">      background: var(--card-bg)</span><br><span class="line">      content: &#x27;&#x27;</span><br><span class="line">      line-height: h</span><br><span class="line">      transition: all .2s ease-in-out</span><br><span class="line"></span><br><span class="line">    &amp;:after</span><br><span class="line">      position: absolute</span><br><span class="line">      bottom: 0</span><br><span class="line">      left: 0</span><br><span class="line">      z-index: 0</span><br><span class="line">      width: 2px</span><br><span class="line">      height: 1.5em</span><br><span class="line">      background: lighten($light-blue, 20)</span><br><span class="line">      content: &#x27;&#x27;</span><br><span class="line"></span><br><span class="line">  &amp;-item</span><br><span class="line">    position: relative</span><br><span class="line">    display: flex</span><br><span class="line">    align-items: center</span><br><span class="line">    margin: 0 0 20px 10px</span><br><span class="line">    transition: all .2s ease-in-out</span><br><span class="line"></span><br><span class="line">    &amp;:hover</span><br><span class="line">      &amp;:before</span><br><span class="line">        border-color: var(--pseudo-hover)</span><br><span class="line"></span><br><span class="line">    &amp;:before</span><br><span class="line">      $w = 6px</span><br><span class="line">      position: absolute</span><br><span class="line">      left: calc(-20px - 17px)</span><br><span class="line">      width: w = $w</span><br><span class="line">      height: h = w</span><br><span class="line">      border: .5 * w solid $light-blue</span><br><span class="line">      border-radius: w</span><br><span class="line">      background: var(--card-bg)</span><br><span class="line">      content: &#x27;&#x27;</span><br><span class="line">      transition: all .2s ease-in-out</span><br><span class="line"></span><br><span class="line">    &amp;.no-article-cover</span><br><span class="line">      height: 80px</span><br><span class="line"></span><br><span class="line">      .article-sort-item-info</span><br><span class="line">        padding: 0</span><br><span class="line"></span><br><span class="line">    &amp;.year</span><br><span class="line">      font-size: 1.43em</span><br><span class="line"></span><br><span class="line">      &amp;:hover</span><br><span class="line">        &amp;:before</span><br><span class="line">          border-color: $light-blue</span><br><span class="line"></span><br><span class="line">      &amp;:before</span><br><span class="line">        border-color: var(--pseudo-hover)</span><br><span class="line"></span><br><span class="line">    &amp;-time</span><br><span class="line">      color: $theme-meta-color</span><br><span class="line">      font-size: 95%</span><br><span class="line"></span><br><span class="line">      time</span><br><span class="line">        padding-left: 6px</span><br><span class="line">        cursor: default</span><br><span class="line"></span><br><span class="line">    &amp;-title</span><br><span class="line">      @extend .limit-more-line</span><br><span class="line">      color: var(--font-color)</span><br><span class="line">      font-size: 1.1em</span><br><span class="line">      transition: all .3s</span><br><span class="line">      -webkit-line-clamp: 2</span><br><span class="line"></span><br><span class="line">      &amp;:hover</span><br><span class="line">        color: $text-hover</span><br><span class="line">        transform: translateX(10px)</span><br><span class="line"></span><br><span class="line"><span class="addition">+   &amp;-index</span></span><br><span class="line"><span class="addition">+     opacity: .5</span></span><br><span class="line"><span class="addition">+     position: absolute</span></span><br><span class="line"><span class="addition">+     top: .5rem</span></span><br><span class="line"><span class="addition">+     right: .5rem</span></span><br><span class="line"><span class="addition">+     font-style: italic</span></span><br><span class="line"><span class="addition">+     font-size: 2.5rem</span></span><br><span class="line"><span class="addition">+     line-height: 1.5rem</span></span><br><span class="line"></span><br><span class="line">    &amp;-img</span><br><span class="line">      overflow: hidden</span><br><span class="line">      width: 80px</span><br><span class="line">      height: 80px</span><br><span class="line"></span><br><span class="line">      img</span><br><span class="line">        @extend .imgHover</span><br><span class="line"></span><br><span class="line">    &amp;-info</span><br><span class="line">      flex: 1</span><br><span class="line">      padding: 0 16px</span><br></pre></td></tr></table></figure></p><h2 id="Hexo-三连"><a href="#Hexo-三连" class="headerlink" title="Hexo 三连"></a>Hexo 三连</h2><p>执行 Hexo 三连</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo g &amp;&amp; hexo s</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.eurkon.com/tags/Butterfly/">Butterfly</category>
      
      
      <comments>https://blog.eurkon.com/post/27df86b.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Butterffly 分类页和标签页隐藏侧栏</title>
      <link>https://blog.eurkon.com/post/d498d8b1.html</link>
      <guid>https://blog.eurkon.com/post/d498d8b1.html</guid>
      <pubDate>Wed, 20 Jul 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;2022-08-04 更新文章&lt;/code&gt; butterfly4.3.0 版本已增加 &lt;code&gt;t</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p><code>2022-08-04 更新文章</code> butterfly4.3.0 版本已增加 <code>tag</code> 和 <code>category</code> 页面可隐藏 <code>aside</code>，但不能隐藏某个 <code>标签</code> 或 <code>分类</code> 的侧边栏。</p></blockquote><p>本文教程主要针对 Hexo Butterfly 主题博客，当用户进入归档、分类、标签页面时，已经是有目的性地浏览文章，此时可以隐藏侧边栏，避免 归档、分类、标签 信息的重复显示，<code>_config.butterfly.yml</code> 配置文件已经有了 归档页 的设置项了，我们需要加上 <code>分类页</code> 和 <code>标签页</code> 的设置项，修改主题源码。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/cover/butterfly_hide_side.png" alt="Butterfly 分类页和标签页隐藏侧栏"></p><h2 id="修改主题配置文件"><a href="#修改主题配置文件" class="headerlink" title="修改主题配置文件"></a>修改主题配置文件</h2><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab">butterfly4.2.2</button><button type="button" class="tab active">butterfly4.3.0</button></div><div class="tab-contents"><div class="tab-item-content"><p>打开 <code>_config.butterfly.yml</code> 文件，找到 <code>aside</code> 配置项，添加 <code>分类页</code> 和 <code>标签页</code> 是否显示侧栏的设置项。</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">  aside:</span><br><span class="line">    enable: true</span><br><span class="line">    hide: false</span><br><span class="line">    button: true</span><br><span class="line">    mobile: true # display on mobile</span><br><span class="line">    position: right # left or right</span><br><span class="line">    archives: false # 归档页隐藏侧栏</span><br><span class="line"><span class="addition">+   categories: false # 分类页隐藏侧栏</span></span><br><span class="line"><span class="addition">+   tags: false # 标签页隐藏侧栏</span></span><br><span class="line">    card_author:</span><br><span class="line">      enable: true</span><br><span class="line">      description:</span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content active"><p>打开 <code>_config.butterfly.yml</code> 文件，找到 <code>aside</code> 配置项，添加 <code>友链页</code> 是否显示侧栏的设置项。</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">  aside:</span><br><span class="line">    enable: true</span><br><span class="line">    hide: false</span><br><span class="line">    button: true</span><br><span class="line">    mobile: true # display on mobile</span><br><span class="line">    position: right # left or right</span><br><span class="line">    display:</span><br><span class="line">      archive: false # 归档页隐藏侧栏</span><br><span class="line">      tag: false # 分类页隐藏侧栏</span><br><span class="line">      category: false # 标签页隐藏侧栏</span><br><span class="line"><span class="addition">+     flink: false # 友链页隐藏侧栏</span></span><br><span class="line">    card_author:</span><br><span class="line">      enable: true</span><br><span class="line">      description:</span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><h2 id="修改主题源码文件"><a href="#修改主题源码文件" class="headerlink" title="修改主题源码文件"></a>修改主题源码文件</h2><p>打开主题源码文件 <code>\themes\butterfly\layout\includes\layout.pug</code>，修改渲染文件。</p><ul><li>is_category：检查当前页面是否为分类归档页面。</li><li>is_tag：检查当前页面是否为标签归档页面。</li><li>page.type 为 index.md 的 type 属性，可以在 <code>\categories\index.md</code> 和 <code>\tags\index.md</code> 目录找到，同理可以 添加 友链页 <code>page.type = &#39;link&#39;</code> 是否显示侧栏配置项。</li></ul><blockquote><p>butterfly4.3.0 版本已增加 <code>tag</code> 和 <code>category</code> 页面可隐藏 <code>aside</code>，但不能隐藏某个 标签 或 分类 的侧边栏。</p><p>可以通过增加 <code>page.type === &#39;categories&#39;</code> 或 <code>page.type === &#39;tags&#39;</code> 来实现隐藏某个 标签 或 分类 的侧边栏。</p></blockquote><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">    - var htmlClassHideAside = theme.aside.enable &amp;&amp; theme.aside.hide ? &#x27;hide-aside&#x27; : &#x27;&#x27;</span><br><span class="line"><span class="deletion">-   - page.aside = is_archive() ? theme.aside.display.archive: is_category() ? theme.aside.display.category : is_tag() ? theme.aside.display.tag : page.aside</span></span><br><span class="line"><span class="addition">+   - page.aside = is_archive() ? theme.aside.archives : page.aside // - 归档页隐藏侧栏</span></span><br><span class="line"><span class="addition">+   - page.aside = is_category() || page.type === &#x27;categories&#x27; ? theme.aside.categories : page.aside // - 分类页隐藏侧栏</span></span><br><span class="line"><span class="addition">+   - page.aside = is_tag() || page.type === &#x27;tags&#x27; ? theme.aside.tags : page.aside // - 标签页隐藏侧栏</span></span><br><span class="line"><span class="addition">+   - page.aside = page.type === &#x27;link&#x27; ? theme.aside.display.flink : page.aside // - 友链页隐藏侧栏</span></span><br><span class="line">    - var hideAside = !theme.aside.enable || page.aside <span class="comment">=== false ? &#x27;hide-aside&#x27; : &#x27;&#x27;</span></span><br><span class="line">    - var pageType = is_post() ? &#x27;post&#x27; : &#x27;page&#x27;</span><br></pre></td></tr></table></figure></p><p>另外可以参看 Hexo 自带判断页面类型的函数，详见 <a href="https://hexo.io/zh-cn/docs/helpers.html#%E6%9D%A1%E4%BB%B6%E5%87%BD%E6%95%B0">条件函数</a> 自行添加其他配置项。</p><h2 id="Hexo-三连"><a href="#Hexo-三连" class="headerlink" title="Hexo 三连"></a>Hexo 三连</h2><p>执行 Hexo 三连</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo g &amp;&amp; hexo s</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.eurkon.com/tags/Butterfly/">Butterfly</category>
      
      
      <comments>https://blog.eurkon.com/post/d498d8b1.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 时间极坐标</title>
      <link>https://blog.eurkon.com/post/97890f7.html</link>
      <guid>https://blog.eurkon.com/post/97890f7.html</guid>
      <pubDate>Tue, 05 Jul 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 640px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 640px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': '日期',    'data': [      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Sun',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Mon',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Tue',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Wed',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Thu',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Fri',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat',      'Sat'    ]  },  {    'name': '时间',    'data': [      0,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      0,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      0,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      0,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      0,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      0,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      0,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23    ]  },  {    'name': 'value',    'data': [      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4,      5,      4,      3,      2,      3,      4    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar seriesData = []var seriesList = [...new Set(data[0].data)]var axisList = [...new Set(data[1].data)]for (let i = 0; i < rowNum; i++) {  seriesData.push([seriesList.indexOf(data[0].data[i]), axisList.indexOf(data[1].data[i]), data[2].data[i]])}var option = {  color: config.colors,  legend: {    show: true,    data: seriesList  },  polar: {    radius: ['10%', '80%']  },  tooltip: {    formatter: function (params) {      return params.marker + seriesList[params.value[0]] + '<br>' + axisList[params.value[1]] + " o'clock" + '<div style="float:right; margin-left: 30px;font-weight:bold;">' + params.value[2] + '</div>';    }  },  angleAxis: {    type: 'category',    data: axisList,    boundaryGap: false,    splitLine: {      show: true    },    axisLabel: {      interval: 0    },    axisLine: {      show: false    }  },  radiusAxis: {    type: 'category',    data: seriesList,    axisLine: {      show: false    },    axisTick: {      show: false    },    axisLabel: {      show: false    }  },  series: []};for (let i = 0; i < seriesList.length; i++) {  option.series.push({    name: seriesList[i],    type: 'scatter',    coordinateSystem: 'polar',    symbolSize: function (val) {      return val[2] * 3;    },    data: seriesData.filter(function (item) {      return item[0] === i;    }),    animationDelay: function (idx) {      return idx * 5;    }  });}chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/97890f7.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 日期旭日图</title>
      <link>https://blog.eurkon.com/post/9bcf49c7.html</link>
      <guid>https://blog.eurkon.com/post/9bcf49c7.html</guid>
      <pubDate>Tue, 21 Jun 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 640px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 640px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': '月',    'data': [      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      4,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      5,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      6,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      7,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      8,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      9,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      10,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      11,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12,      12    ]  },  {    'name': '日',    'data': [      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      31,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      31,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      31,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      31,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      31,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      31,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      1,      2,      3,      4,      5,      6,      7,      8,      9,      10,      11,      12,      13,      14,      15,      16,      17,      18,      19,      20,      21,      22,      23,      24,      25,      26,      27,      28,      29,      30,      31    ]  },  {    'name': '数值',    'data': [      1,      75,      111,      228,      157,      137,      173,      308,      261,      201,      241,      281,      160,      172,      199,      256,      251,      269,      173,      161,      170,      169,      162,      223,      198,      183,      130,      96,      130,      116,      45,      93,      28,      65,      43,      47,      98,      43,      46,      23,      17,      61,      82,      160,      75,      77,      77,      174,      160,      155,      94,      134,      203,      175,      160,      159,      185,      164,      217,      46,      240,      257,      154,      175,      267,      226,      244,      201,      216,      142,      209,      177,      129,      258,      280,      248,      136,      161,      225,      220,      226,      267,      353,      91,      213,      225,      216,      242,      281,      222,      267,      162,      200,      96,      100,      68,      139,      195,      340,      177,      145,      172,      175,      178,      175,      171,      209,      128,      265,      308,      173,      180,      164,      234,      264,      319,      229,      253,      277,      242,      296,      72,      165,      233,      223,      211,      230,      207,      394,      189,      190,      160,      180,      235,      193,      222,      226,      223,      126,      242,      212,      277,      221,      207,      258,      132,      234,      279,      278,      251,      263,      194,      115,      185,      206,      229,      265,      123,      230,      135,      172,      240,      133,      198,      175,      290,      137,      255,      189,      242,      164,      234,      173,      116,      217,      239,      264,      193,      217,      224,      312,      107,      126,      145,      167,      160,      172,      175,      170,      165,      225,      123,      275,      167,      90,      133,      168,      187,      154,      216,      210,      103,      132,      202,      203,      279,      336,      190,      191,      142,      181,      175,      336,      181,      223,      129,      150,      210,      163,      192,      156,      243,      192,      154,      164,      271,      192,      254,      222,      187,      185,      256,      228,      163,      266,      160,      195,      125,      206,      255,      240,      189,      263,      179,      118,      152,      161,      237,      137,      186,      169,      130,      126,      158,      218,      168,      180,      210,      70,      126,      202,      199,      219,      200,      235,      167,      138,      309,      217,      267,      163,      312,      197,      25,      89,      62,      96,      163,      216,      250,      271,      266,      159,      172,      279,      313,      313,      196,      292,      208,      203,      277,      215,      255,      258,      316,      189,      205,      230,      299,      308,      137,      226,      161,      164,      125,      300,      270,      244,      248,      221,      249,      277,      261,      243,      374,      348,      203,      176,      227,      243,      259,      349,      264,      257,      209,      272,      255,      285,      189,      293,      155,      173,      240,      263,      162,      257,      339,      268,      190,      207,      318,      195,      259,      186,      212,      282,      210,      288,      215,      209,      251,      213,      203,      194,      361,      267,      182,      172,      157,      327,      208    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar seriesData = []var parent = [...new Set(data[0].data)]var children = [...new Set(data[1].data)]for (let i = 0; i < parent.length; i++) {  let temp = []  for (let j = 0; j < children.length; j++) {    temp.push({ 'name': children[j], 'value': 0 })  }  seriesData.push(temp)}for (let i = 0; i < rowNum; i++) {  seriesData[parent.indexOf(data[0].data[i])][children.indexOf(data[1].data[i])].value = data[2].data[i]}var option = {  color: config.colors,  legend: {    show: false,    data: parent,    itemStyle: {      color: config.colors[0]    }  },  tooltip: {},  visualMap: [    {      show: true,      type: 'continuous',      calculable: true,      min: 0,      max: Math.max(...new Set(data[2].data)),      inRange: {        color: ['#fff', config.colors[0]]      },      text: ['High', 'Low']    },  ],  series: []};var radius = 90 / parent.lengthfor (let i = 0; i < parent.length; i++) {  option.series.push({    name: parent[i] + data[0].name,    type: 'pie',    roseType: 'area',    radius: [10 + radius * i + '%', 10 + radius * (i + 1) + '%'],    label: {      show: i === parent.length - 1,      position: 'inside'    },    itemStyle: {      borderColor: 'rgba(255, 255, 255, 0.5)',      borderWidth: 2    },    emphasis: {      scale: false,      label: {        show: true      }    },    data: seriesData[i]  });}chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/9bcf49c7.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 对比漏斗图</title>
      <link>https://blog.eurkon.com/post/ee94ffd8.html</link>
      <guid>https://blog.eurkon.com/post/ee94ffd8.html</guid>
      <pubDate>Fri, 10 Jun 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container1, #container2 {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container1, #container2 {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container1"></div></p><p><div id="container2"></div></p><p><script defer data-pjax>var data1 = [  {    'name': '指标',    'data': [      '展示',      '点击',      '咨询',      '下单'    ]  },  {    'name': '天猫',    'data': [      3532093380,      1462451270,      639296573,      318559381    ]  }]var data2 = [  {    'name': '指标',    'data': [      '展示',      '点击',      '咨询',      '下单'    ]  },  {    'name': '天猫',    'data': [      3532093380,      1462451270,      639296573,      318559381    ]  },  {    'name': '京东',    'data': [      2839302314,      1045722440,      397406636,      121552402    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  ratio: true, // 是否显示占比  transform: true, // 是否显示转化率  legendShow: true, // 是否显示图例位置  legendPosition: 'top', // 'top', 'middle', 'bottom' 图例位置}function render (id, data, config) {  var container =  document.getElementById(id)  var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })  var rowNum = data[0].data.length  var colNum = data.length  var seriesData1 = []  var seriesData2 = []  var maxList = [0, 0]  var maxSum = 0  for (let i = 0; i < rowNum; i++) {    seriesData1.push({      'name': data[0].data[i], 'value': data[1].data[i],      'nextName': i === rowNum - 1 ? '' : data[0].data[i + 1],      'nextValue': i === rowNum - 1 ? '' : data[1].data[i + 1],      'transform': i === rowNum - 1 ? '' : (100 * data[1].data[i + 1] / data[1].data[i]).toFixed(2)    })    if (colNum > 2) {      seriesData2.push({        'name': data[0].data[i], 'value': data[2].data[i],        'nextName': i === rowNum - 1 ? '' : data[0].data[i + 1],        'nextValue': i === rowNum - 1 ? '' : data[2].data[i + 1],        'transform': i === rowNum - 1 ? '' : (100 * data[2].data[i + 1] / data[2].data[i]).toFixed(2)      })      let valueSum = data[1].data[i] + data[2].data[i]      if (valueSum > maxSum) { maxSum = valueSum; maxList = [data[1].data[i], data[2].data[i]] }    }  }  function numberFormat (value) {    let k = 10000    let sizes = ['', '万', '亿', '万亿']    if (value >= k) {      let i = Math.floor(Math.log(value) / Math.log(k));      return (value / Math.pow(k, i)).toFixed(2) + sizes[i];    }    return Number(value).toFixed(2);  }  var option = {    color: config.colors,    legend: {      show: config.legendShow,      top: config.legendPosition,      data: colNum > 2 ? [data[1].name, data[2].name] : data[0].data    },    tooltip: {      trigger: 'item',      confine: true,      formatter: function (params) {        let res = []        let style = '<div style="float:right;margin-left:30px;font-weight:bold;">'        res.push(params.seriesName)        res.push(params.marker + params.name + style + numberFormat(params.value) + '</div>')        if (config.transform && params.data.transform) {          res.push(params.marker + params.data.nextName + style + numberFormat(params.data.nextValue) + '</div>')          res.push(params.marker + '转化率' + style + params.data.transform + '%</div>')        }        if (config.ratio) { res.push(params.marker + '占比' + style + params.percent + '%</div>') }        return res.join('<br>')      }    },    series: []  }  if (colNum > 2) {    let maxValue = Math.max(...maxList) || 1    option.series.push({      name: data[1].name,      type: 'funnel',      colorBy: 'series',      sort: config.sort || 'descending',      width: (40 * maxList[0] / maxValue).toFixed(2) + '%',      left: (50 - 40 * maxList[0] / maxValue).toFixed(2) + '%',      top: '8%',      height: '88%',      funnelAlign: 'right',      label: {        position: 'left',        formatter: function (params) {          let res = []          res.push(params.name + '：' + numberFormat(params.value))          if (config.ratio) { res.push('占比：' + params.percent + '%') }          return res.join('\n')        }      },      labelLine: {        length: 30,      },      data: seriesData1    });    option.series.push({      name: data[2].name,      type: 'funnel',      colorBy: 'series',      sort: config.sort || 'descending',      width: (40 * maxList[1] / maxValue).toFixed(2) + '%',      left: '50%',      top: '8%',      height: '88%',      funnelAlign: 'left',      label: {        position: 'right',        formatter: function (params) {          let res = []          res.push(params.name + '：' + numberFormat(params.value))          if (config.ratio) { res.push('占比：' + params.percent + '%') }          return res.join('\n')        }      },      labelLine: {        length: 30,      },      data: seriesData2    });  } else {    option.series.push({      name: data[1].name,      type: 'funnel',      sort: config.sort || 'descending',      top: '8%',      height: '88%',      label: {        position: 'right',        formatter: function (params) {          let res = []          res.push(params.name + '：' + numberFormat(params.value))          if (config.ratio) { res.push('占比：' + params.percent + '%') }          return res.join('\n')        }      },      labelLine: {        length: 30,      },      data: seriesData1    });  }  chart.setOption(option);  window.addEventListener('resize', () => {    chart.resize();  });}render ('container1', data1, config)render ('container2', data2, config)</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/ee94ffd8.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Butterfly 分类标签导航栏</title>
      <link>https://blog.eurkon.com/post/65b72006.html</link>
      <guid>https://blog.eurkon.com/post/65b72006.html</guid>
      <pubDate>Mon, 23 May 2022 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文用于 butterfly 魔改，博主没有测试是否适配于其他主题，以及自定义样式 CSS 可能需要一定的前端知识进行优化。&lt;/p&gt;
&lt;bl</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文用于 butterfly 魔改，博主没有测试是否适配于其他主题，以及自定义样式 CSS 可能需要一定的前端知识进行优化。</p><blockquote><ul><li><code>2022-08-15</code> 适配多分类文章，优化代码</li><li><code>2022-08-16</code> 当分类/标签较多时，导航栏会滚动至当前页面高亮标签。</li><li><code>2023-03-21</code> 修复分类/标签翻页导致无法定位当前分类/标签的问题</li></ul></blockquote><h2 id="效果预览"><a href="#效果预览" class="headerlink" title="效果预览"></a>效果预览</h2><p><strong>分类导航栏</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/butterfly/butterfly_categories_list.png" alt="分类导航栏"></p><p><strong>标签导航栏</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/butterfly/butterfly_tags_list.png" alt="标签导航栏"></p><h2 id="新建-catalog-list-js"><a href="#新建-catalog-list-js" class="headerlink" title="新建 catalog_list.js"></a>新建 catalog_list.js</h2><p><code>[Blogroot]\themes\butterfly\scripts\helpers\</code> 目录下新建文件 <code>catalog_list.js</code>，<code>type</code> 参数表示生成 分类导航栏 <code>categories</code> 还是 标签导航栏 <code>tags</code>，其中 <code>&lt;sup&gt;$&#123;item.length&#125;&lt;/sup&gt;</code> 是使用上标显示文章数量，可参考<a href="/post/6687849c.html">Butterfly 标签云增加文章数上下标</a>。</p><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">hexo.extend.helper.register(<span class="string">&#x27;catalog_list&#x27;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">type</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> html = <span class="string">``</span></span><br><span class="line">  hexo.locals.get(type).map(<span class="function"><span class="keyword">function</span> (<span class="params">item</span>) </span>&#123;</span><br><span class="line">    html += <span class="string">`</span></span><br><span class="line"><span class="string">    &lt;div class=&quot;catalog-list-item&quot; id=&quot;/<span class="subst">$&#123;item.path&#125;</span>&quot;&gt;</span></span><br><span class="line"><span class="string">      &lt;a href=&quot;/<span class="subst">$&#123;item.path&#125;</span>&quot;&gt;<span class="subst">$&#123;item.name&#125;</span>&lt;sup&gt;<span class="subst">$&#123;item.length&#125;</span>&lt;/sup&gt;&lt;/a&gt;</span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">    `</span></span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="keyword">return</span> html</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p><p><strong>注</strong>：如果之前有问过博主如何 显示 分类/标签 时带 emoji，但相应 分类/标签 页面不带 emoji 的小伙伴可以修改为：</p><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">hexo.extend.helper.register(<span class="string">&#x27;catalog_list&#x27;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">type</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> html = <span class="string">``</span></span><br><span class="line">  hexo.locals.get(type).map(<span class="function"><span class="keyword">function</span> (<span class="params">item</span>) </span>&#123;</span><br><span class="line">    html += <span class="string">`</span></span><br><span class="line"><span class="string">    &lt;div class=&quot;catalog-list-item&quot; id=&quot;/<span class="subst">$&#123;item.path&#125;</span>&quot;&gt;</span></span><br><span class="line"><span class="string">      &lt;a href=&quot;/<span class="subst">$&#123;item.path&#125;</span>&quot;&gt;<span class="subst">$&#123;(hexo.config.emoji &amp;&amp; hexo.config.emoji[item.name] || <span class="string">&#x27;&#x27;</span>) + item.name&#125;</span>&lt;sup&gt;<span class="subst">$&#123;item.length&#125;</span>&lt;/sup&gt;&lt;/a&gt;</span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">    `</span></span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="keyword">return</span> html</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p><h2 id="新增自定义样式"><a href="#新增自定义样式" class="headerlink" title="新增自定义样式"></a>新增自定义样式</h2><p>其中部分是博主魔改后的样式，如 <code>--main: #1677B3</code>、<code>--second: #fff</code>、<code>--card-border: 1px solid rgba(150,150,150,0.2);</code>、<code>--light-text: #ff7242</code>、<code>--border-radius: .5rem</code>。</p><p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 分类目录条、标签目录条 */</span></span><br><span class="line"><span class="selector-id">#catalog-bar</span> &#123;</span><br><span class="line">  <span class="attribute">padding</span>: .<span class="number">4rem</span> .<span class="number">8rem</span>;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="built_in">var</span>(--border-radius);</span><br><span class="line">  <span class="attribute">display</span>: flex;</span><br><span class="line">  <span class="attribute">border</span>: <span class="built_in">var</span>(--card-border);</span><br><span class="line">  <span class="attribute">margin-bottom</span>: <span class="number">1rem</span>;</span><br><span class="line">  <span class="attribute">justify-content</span>: space-between;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#catalog-bar</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">border-color</span>: <span class="built_in">var</span>(--main);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#catalog-bar</span> <span class="selector-tag">i</span> &#123;</span><br><span class="line">  <span class="attribute">line-height</span>: inherit;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#catalog-list</span> &#123;</span><br><span class="line">  <span class="comment">/* 分类/标签较少时，可以选择不设置 width，居中显示 catalog-list-item */</span></span><br><span class="line">  <span class="comment">/* width: 100%; */</span></span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">0</span> .<span class="number">5rem</span>;</span><br><span class="line">  <span class="attribute">display</span>: flex;</span><br><span class="line">  <span class="attribute">white-space</span>: nowrap;</span><br><span class="line">  <span class="attribute">overflow-x</span>: scroll;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#catalog-list</span><span class="selector-pseudo">::-webkit-scrollbar</span> &#123;</span><br><span class="line">  <span class="attribute">display</span>: none;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.catalog-list-item</span> <span class="selector-tag">a</span> &#123;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">0</span> .<span class="number">2em</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">0.2em</span> <span class="number">0.3em</span> <span class="number">0.3em</span>;</span><br><span class="line">  <span class="attribute">font-weight</span>: bold;</span><br><span class="line">  <span class="attribute">border-radius</span>: <span class="built_in">var</span>(--border-radius);</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--font-color);</span><br><span class="line">  <span class="attribute">-webkit-transition</span>: all .<span class="number">3s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-moz-transition</span>: all .<span class="number">3s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-o-transition</span>: all .<span class="number">3s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">-ms-transition</span>: all .<span class="number">3s</span> ease-in-out;</span><br><span class="line">  <span class="attribute">transition</span>: all .<span class="number">3s</span> ease-in-out;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.catalog-list-item</span><span class="selector-pseudo">:hover</span> <span class="selector-tag">a</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">var</span>(--main);</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--second);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.catalog-list-item</span><span class="selector-class">.selected</span> <span class="selector-tag">a</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">var</span>(--light-text);</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--second);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">a</span><span class="selector-class">.catalog-more</span> &#123;</span><br><span class="line">  <span class="attribute">min-width</span>: fit-content;</span><br><span class="line">  <span class="attribute">font-weight</span>: bold;</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--font-color);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">a</span><span class="selector-class">.catalog-more</span><span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: <span class="built_in">var</span>(--main);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="当前所在分类-标签页高亮"><a href="#当前所在分类-标签页高亮" class="headerlink" title="当前所在分类/标签页高亮"></a>当前所在分类/标签页高亮</h2><p>自定义 js 增加以下内容：</p><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">catalogActive</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> $list = <span class="built_in">document</span>.getElementById(<span class="string">&#x27;catalog-list&#x27;</span>)</span><br><span class="line">  <span class="keyword">if</span> ($list) &#123;</span><br><span class="line">    <span class="comment">// 鼠标滚轮滚动</span></span><br><span class="line">    $list.addEventListener(<span class="string">&#x27;mousewheel&#x27;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 计算鼠标滚轮滚动的距离</span></span><br><span class="line">      $list.scrollLeft -= e.wheelDelta / <span class="number">2</span></span><br><span class="line">      <span class="comment">// 阻止浏览器默认方法</span></span><br><span class="line">      e.preventDefault()</span><br><span class="line">    &#125;, <span class="literal">false</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 高亮当前页面对应的分类或标签</span></span><br><span class="line">    <span class="keyword">let</span> path = <span class="built_in">decodeURIComponent</span>(<span class="built_in">window</span>.location.pathname).replace(<span class="regexp">/page\/[0-9]+\//g</span>, <span class="string">&#x27;&#x27;</span>)</span><br><span class="line">    <span class="keyword">let</span> $catalog = <span class="built_in">document</span>.getElementById(path)</span><br><span class="line">    $catalog?.classList.add(<span class="string">&#x27;selected&#x27;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 滚动当前页面对应的分类或标签到中部</span></span><br><span class="line">    $list.scrollLeft = ($catalog.offsetLeft - $list.offsetLeft) - ($list.offsetWidth - $catalog.offsetWidth) / <span class="number">2</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">catalogActive()</span><br></pre></td></tr></table></figure></p><h2 id="使用分类导航栏"><a href="#使用分类导航栏" class="headerlink" title="使用分类导航栏"></a>使用分类导航栏</h2><p><code>[Blogroot]\themes\butterfly\layout\category.pug</code> 增加以下内容：</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">extends includes/layout.pug</span><br><span class="line"></span><br><span class="line">block content</span><br><span class="line">  if theme.category_ui == &#x27;index&#x27;</span><br><span class="line">    include ./includes/mixins/post-ui.pug</span><br><span class="line">    #recent-posts.recent-posts.category_ui</span><br><span class="line">      +postUI</span><br><span class="line">      include includes/pagination.pug</span><br><span class="line">  else</span><br><span class="line">    include ./includes/mixins/article-sort.pug</span><br><span class="line">    #category</span><br><span class="line"><span class="addition">+     #catalog-bar</span></span><br><span class="line"><span class="addition">+       i.fa-fw.fas.fa-shapes</span></span><br><span class="line"><span class="addition">+       #catalog-list</span></span><br><span class="line"><span class="addition">+         !=catalog_list(&quot;categories&quot;)</span></span><br><span class="line"><span class="addition">+       a.catalog-more(href=&quot;/categories/&quot;)!= &#x27;更多&#x27;</span></span><br><span class="line">      .article-sort-title= _p(&#x27;page.category&#x27;) + &#x27; - &#x27; + page.category</span><br><span class="line">      +articleSort(page.posts)</span><br><span class="line">      include includes/pagination.pug</span><br></pre></td></tr></table></figure></p><h2 id="使用标签导航栏"><a href="#使用标签导航栏" class="headerlink" title="使用标签导航栏"></a>使用标签导航栏</h2><p><code>[Blogroot]\themes\butterfly\layout\tag.pug</code> 增加以下内容：</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">extends includes/layout.pug</span><br><span class="line"></span><br><span class="line">block content</span><br><span class="line">  if theme.tag_ui == &#x27;index&#x27;</span><br><span class="line">    include ./includes/mixins/post-ui.pug</span><br><span class="line">    #recent-posts.recent-posts</span><br><span class="line">      +postUI</span><br><span class="line">      include includes/pagination.pug</span><br><span class="line">  else</span><br><span class="line">    include ./includes/mixins/article-sort.pug</span><br><span class="line">    #tag</span><br><span class="line"><span class="addition">+     #catalog-bar</span></span><br><span class="line"><span class="addition">+       i.fa-fw.fas.fa-tags</span></span><br><span class="line"><span class="addition">+       #catalog-list</span></span><br><span class="line"><span class="addition">+         !=catalog_list(&quot;tags&quot;)</span></span><br><span class="line"><span class="addition">+       a.catalog-more(href=&quot;/tags/&quot;)!= &#x27;更多&#x27;</span></span><br><span class="line">      .article-sort-title= _p(&#x27;page.tag&#x27;) + &#x27; - &#x27; + page.tag</span><br><span class="line">      +articleSort(page.posts)</span><br><span class="line">      include includes/pagination.pug</span><br></pre></td></tr></table></figure></p><h2 id="Hexo-三连"><a href="#Hexo-三连" class="headerlink" title="Hexo 三连"></a>Hexo 三连</h2><p>执行 Hexo 三连</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo g &amp;&amp; hexo s</span><br></pre></td></tr></table></figure></p><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><blockquote><ol><li><strong>张洪Heo</strong> <a href="https://blog.zhheo.com/p/bc61964d.html">Butterfly魔改：动态分类条，可以根据页面变化而改变的分类列表展示方式</a></li></ol></blockquote>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.eurkon.com/tags/Butterfly/">Butterfly</category>
      
      
      <comments>https://blog.eurkon.com/post/65b72006.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 水球图</title>
      <link>https://blog.eurkon.com/post/51a3160b.html</link>
      <guid>https://blog.eurkon.com/post/51a3160b.html</guid>
      <pubDate>Mon, 16 May 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 600px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 600px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    name: '',    data: []  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar option = {  series: [    {      type: 'liquidFill',      data: [0.6, 0.5, 0.4, 0.3],      radius: '40%',      shape: 'diamond',      center: ['25%', '25%'],    },    {      type: 'liquidFill',      data: [0.6, 0.5, 0.4, 0.3],      direction: 'left',      radius: '40%',      shape: 'rect',      center: ['75%', '25%'],    },    {      type: 'liquidFill',      data: [0.6, 0.5, 0.4, 0.3],      radius: '40%',      shape: 'roundRect',      center: ['25%', '75%'],      backgroundStyle: {        borderColor: '#156ACF',        borderWidth: 1,        shadowColor: 'rgba(0, 0, 0, 0.4)',        shadowBlur: 20,      },      outline: {        show: false, // 外边框      },      waveAnimation: true, // 左右波动    },    {      type: 'liquidFill',      data: [0.6, 0.5, 0.4, 0.3],      radius: '50%',      shape: 'pin',      center: ['75%', '75%'],      // amplitude: 0,      waveAnimation: true,      outline: {        show: false,      },      backgroundStyle: {        shadowColor: 'rgba(0, 0, 0, 0.4)',        shadowBlur: 20,      },    },  ],};chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/51a3160b.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 词云图</title>
      <link>https://blog.eurkon.com/post/f6f4d480.html</link>
      <guid>https://blog.eurkon.com/post/f6f4d480.html</guid>
      <pubDate>Fri, 13 May 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': 'word',    'data': [      'Macys',      'Amy Schumer',      'Jurassic World',      'Charter Communications',      'Chick Fil A',      'Planet Fitness',      'Pitch Perfect',      'Express',      'Home',      'Johnny Depp',      'Lena Dunham',      'Lewis Hamilton',      'KXAN',      'Mary Ellen Mark',      'Farrah Abraham',      'Rita Ora',      'Serena Williams',      'NCAA baseball tournament',      'Point Break'    ]  },  {    'name': 'value',    'data': [      6181,      4386,      4055,      2467,      2244,      1898,      1484,      1112,      965,      847,      582,      555,      550,      462,      366,      360,      282,      273,      265    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar seriesData = []for (let i = 0; i < rowNum; i++) {  seriesData.push({ 'name': data[0].data[i], 'value': data[1].data[i] })}var maskImage = new Image();maskImage.src = 'data:image/png;base64,'var option = {  tooltip: {    confine: true  },  series: [{    type: 'wordCloud',    gridSize: 20,    sizeRange: [12, 50],    rotationRange: [-90, 90],    // maskImage: maskImage,    shape: 'star', // 'circle', 'cardioid', 'diamond', 'triangle-forward', 'triangle', 'pentagon', 'star'    width: '100%',    height: '100%',    drawOutOfBound: true,    textStyle: {      color: function () {        return 'rgb(' + [          Math.round(Math.random() * 160),          Math.round(Math.random() * 160),          Math.round(Math.random() * 160)        ].join(',') + ')';      }    },    emphasis: {      textStyle: {        shadowBlur: 10,        shadowColor: '#333'      }    },    data: seriesData  }]}chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/f6f4d480.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 流域图</title>
      <link>https://blog.eurkon.com/post/364f4c67.html</link>
      <guid>https://blog.eurkon.com/post/364f4c67.html</guid>
      <pubDate>Tue, 10 May 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': 'category',    'data': [      '洁面',      '面部精华',      '卸妆',      '防晒',      '身体护理',      '面膜',      '眼霜',      '化妆水爽肤水',      '眼部精华',      '身体护理',      '化妆水爽肤水',      '防晒',      '洁面',      '眼部精华',      '面膜',      '面部精华',      '眼霜',      '卸妆',      '身体护理',      '面膜',      '面部精华',      '卸妆',      '洁面',      '化妆水爽肤水',      '眼霜',      '眼部精华',      '防晒',      '眼部精华',      '卸妆',      '眼霜',      '身体护理',      '化妆水爽肤水',      '防晒',      '面膜',      '洁面',      '面部精华',      '洁面',      '面部精华',      '眼霜',      '防晒',      '身体护理',      '化妆水爽肤水',      '眼部精华',      '面膜',      '卸妆',      '防晒',      '化妆水爽肤水',      '卸妆',      '洁面',      '身体护理',      '眼霜',      '眼部精华',      '面膜',      '面部精华',      '眼霜',      '身体护理',      '防晒',      '洁面',      '卸妆',      '化妆水爽肤水',      '面部精华',      '面膜',      '眼部精华',      '眼霜',      '防晒',      '面膜',      '洁面',      '眼部精华',      '面部精华',      '卸妆',      '化妆水爽肤水',      '身体护理',      '洁面',      '面部精华',      '身体护理',      '化妆水爽肤水',      '防晒',      '卸妆',      '面膜',      '眼霜',      '眼部精华',      '身体护理',      '洁面',      '化妆水爽肤水',      '眼霜',      '防晒',      '眼部精华',      '面膜',      '面部精华',      '卸妆',      '眼霜',      '化妆水爽肤水',      '洁面',      '眼部精华',      '面膜',      '面部精华',      '卸妆',      '防晒',      '身体护理',      '卸妆',      '面部精华',      '化妆水爽肤水',      '眼霜',      '洁面',      '眼部精华',      '身体护理',      '防晒',      '面膜',      '面膜',      '卸妆',      '防晒',      '身体护理',      '洁面',      '眼霜',      '眼部精华',      '面部精华',      '化妆水爽肤水',      '身体护理',      '防晒',      '化妆水爽肤水',      '卸妆',      '洁面',      '眼部精华',      '面部精华',      '面膜',      '眼霜',      '面部精华',      '眼霜',      '身体护理',      '眼部精华',      '面膜',      '洁面',      '卸妆',      '防晒',      '化妆水爽肤水',      '眼部精华',      '眼霜',      '化妆水爽肤水',      '防晒',      '卸妆',      '洁面',      '面部精华',      '面膜',      '身体护理',      '面部精华',      '眼霜',      '卸妆',      '面膜',      '眼部精华',      '洁面',      '防晒',      '化妆水爽肤水',      '身体护理',      '卸妆',      '身体护理',      '眼霜',      '防晒',      '面膜',      '化妆水爽肤水',      '眼部精华',      '面部精华',      '洁面',      '眼霜',      '卸妆',      '化妆水爽肤水',      '防晒',      '洁面',      '面部精华',      '眼部精华',      '面膜',      '身体护理',      '化妆水爽肤水',      '身体护理',      '防晒',      '卸妆',      '眼部精华',      '眼霜',      '面膜',      '洁面',      '面部精华',      '洁面',      '化妆水爽肤水',      '防晒',      '眼霜',      '身体护理',      '卸妆',      '眼部精华',      '面膜',      '面部精华',      '化妆水爽肤水',      '洁面',      '面部精华',      '卸妆',      '眼部精华',      '身体护理',      '面膜',      '眼霜',      '防晒',      '面部精华',      '卸妆',      '身体护理',      '化妆水爽肤水',      '眼部精华',      '防晒',      '眼霜',      '洁面',      '面膜',      '面膜',      '洁面',      '卸妆',      '防晒',      '眼霜',      '化妆水爽肤水',      '身体护理',      '面部精华',      '眼部精华',      '化妆水爽肤水',      '眼霜',      '身体护理',      '防晒',      '眼部精华',      '面部精华',      '卸妆',      '面膜',      '洁面',      '眼部精华',      '面部精华',      '卸妆',      '身体护理',      '洁面',      '面膜',      '防晒',      '化妆水爽肤水',      '眼霜',      '面膜',      '化妆水爽肤水',      '卸妆',      '身体护理',      '眼霜',      '洁面',      '防晒',      '眼部精华',      '面部精华',      '面部精华',      '身体护理',      '眼部精华',      '面膜',      '化妆水爽肤水',      '眼霜',      '防晒',      '卸妆',      '洁面',      '化妆水爽肤水',      '身体护理',      '防晒',      '面部精华',      '卸妆',      '面膜',      '眼霜',      '洁面',      '眼部精华',      '防晒',      '眼部精华',      '面部精华',      '洁面',      '化妆水爽肤水',      '卸妆',      '身体护理',      '眼霜',      '面膜',      '面部精华',      '眼霜',      '洁面',      '卸妆',      '防晒',      '面膜',      '眼部精华',      '化妆水爽肤水',      '身体护理',      '眼霜',      '洁面',      '身体护理',      '化妆水爽肤水',      '面部精华',      '眼部精华',      '卸妆',      '面膜',      '防晒',      '化妆水爽肤水',      '洁面',      '眼部精华',      '眼霜',      '卸妆',      '面膜',      '身体护理',      '防晒',      '面部精华',      '身体护理',      '眼霜',      '卸妆',      '防晒',      '化妆水爽肤水',      '洁面',      '眼部精华',      '面膜',      '面部精华',      '身体护理',      '眼霜',      '面部精华',      '洁面',      '防晒',      '卸妆',      '化妆水爽肤水',      '眼部精华',      '面膜',      '卸妆',      '身体护理',      '眼霜',      '眼部精华',      '防晒',      '面部精华',      '化妆水爽肤水',      '洁面',      '面膜',      '卸妆',      '化妆水爽肤水',      '眼部精华',      '面膜',      '洁面',      '面部精华',      '身体护理',      '防晒',      '眼霜',      '面膜',      '身体护理',      '化妆水爽肤水',      '卸妆',      '面部精华',      '眼霜',      '防晒',      '洁面',      '眼部精华',      '面膜',      '洁面',      '卸妆',      '眼霜',      '化妆水爽肤水',      '身体护理',      '眼部精华',      '面部精华',      '防晒',      '眼部精华',      '化妆水爽肤水',      '洁面',      '身体护理',      '面部精华',      '面膜',      '眼霜',      '卸妆',      '防晒',      '卸妆',      '防晒',      '身体护理',      '眼部精华',      '面膜',      '眼霜',      '洁面',      '化妆水爽肤水',      '面部精华',      '防晒',      '卸妆',      '眼部精华',      '洁面',      '面膜',      '面部精华',      '身体护理',      '眼霜',      '化妆水爽肤水',      '化妆水爽肤水',      '面部精华',      '防晒',      '眼部精华',      '卸妆',      '眼霜',      '洁面',      '面膜',      '身体护理',      '身体护理',      '防晒',      '卸妆',      '面膜',      '眼霜',      '面部精华',      '眼部精华',      '洁面',      '化妆水爽肤水',      '洁面',      '防晒',      '面膜',      '身体护理',      '卸妆',      '化妆水爽肤水',      '眼霜',      '眼部精华',      '面部精华',      '面膜',      '身体护理',      '面部精华',      '洁面',      '眼霜',      '卸妆',      '防晒',      '化妆水爽肤水',      '眼部精华',      '面部精华',      '化妆水爽肤水',      '身体护理',      '面膜',      '眼霜',      '眼部精华',      '洁面',      '防晒',      '卸妆',      '身体护理',      '眼霜',      '面部精华',      '防晒',      '面膜',      '眼部精华',      '洁面',      '卸妆',      '化妆水爽肤水',      '眼霜',      '身体护理',      '面膜',      '面部精华',      '防晒',      '卸妆',      '化妆水爽肤水',      '眼部精华',      '洁面',      '面膜',      '化妆水爽肤水',      '面部精华',      '卸妆',      '身体护理',      '眼部精华',      '防晒',      '洁面',      '眼霜',      '防晒',      '洁面',      '身体护理',      '眼部精华',      '面膜',      '眼霜',      '面部精华',      '卸妆',      '化妆水爽肤水',      '卸妆',      '化妆水爽肤水',      '面膜',      '眼霜',      '身体护理',      '防晒',      '洁面',      '眼部精华',      '面部精华',      '化妆水爽肤水',      '面部精华',      '眼霜',      '洁面',      '面膜',      '眼部精华',      '身体护理',      '防晒',      '卸妆',      '身体护理',      '面部精华',      '眼霜',      '化妆水爽肤水',      '面膜',      '洁面',      '卸妆',      '眼部精华',      '防晒',      '眼霜',      '面膜',      '身体护理',      '面部精华',      '眼部精华',      '化妆水爽肤水',      '防晒',      '洁面',      '卸妆',      '面膜',      '身体护理',      '卸妆',      '眼霜',      '眼部精华',      '面部精华',      '防晒',      '洁面',      '化妆水爽肤水',      '洁面',      '化妆水爽肤水',      '面部精华',      '卸妆',      '身体护理',      '防晒',      '眼部精华',      '眼霜',      '面膜',      '防晒',      '眼霜',      '身体护理',      '面膜',      '眼部精华',      '面部精华',      '洁面',      '化妆水爽肤水',      '卸妆',      '身体护理',      '眼部精华',      '防晒',      '洁面',      '面部精华',      '面膜',      '卸妆',      '化妆水爽肤水',      '眼霜',      '洁面',      '防晒',      '眼部精华',      '卸妆',      '面部精华',      '身体护理',      '化妆水爽肤水',      '眼霜',      '面膜',      '化妆水爽肤水',      '面部精华',      '洁面',      '身体护理',      '防晒',      '眼部精华',      '卸妆',      '面膜',      '眼霜',      '面部精华',      '面膜',      '防晒',      '化妆水爽肤水',      '眼霜',      '卸妆',      '身体护理',      '眼部精华',      '洁面',      '化妆水爽肤水',      '卸妆',      '面膜',      '眼部精华',      '面部精华',      '洁面',      '防晒',      '身体护理',      '眼霜',      '眼霜',      '卸妆',      '面部精华',      '身体护理',      '眼部精华',      '洁面',      '化妆水爽肤水',      '防晒',      '面膜'    ]  },  {    'name': 'data_date',    'data': [      '2017-01-01',      '2017-01-01',      '2017-01-01',      '2017-01-01',      '2017-01-01',      '2017-01-01',      '2017-01-01',      '2017-01-01',      '2017-01-01',      '2017-02-01',      '2017-02-01',      '2017-02-01',      '2017-02-01',      '2017-02-01',      '2017-02-01',      '2017-02-01',      '2017-02-01',      '2017-02-01',      '2017-03-01',      '2017-03-01',      '2017-03-01',      '2017-03-01',      '2017-03-01',      '2017-03-01',      '2017-03-01',      '2017-03-01',      '2017-03-01',      '2017-04-01',      '2017-04-01',      '2017-04-01',      '2017-04-01',      '2017-04-01',      '2017-04-01',      '2017-04-01',      '2017-04-01',      '2017-04-01',      '2017-05-01',      '2017-05-01',      '2017-05-01',      '2017-05-01',      '2017-05-01',      '2017-05-01',      '2017-05-01',      '2017-05-01',      '2017-05-01',      '2017-06-01',      '2017-06-01',      '2017-06-01',      '2017-06-01',      '2017-06-01',      '2017-06-01',      '2017-06-01',      '2017-06-01',      '2017-06-01',      '2017-07-01',      '2017-07-01',      '2017-07-01',      '2017-07-01',      '2017-07-01',      '2017-07-01',      '2017-07-01',      '2017-07-01',      '2017-07-01',      '2017-08-01',      '2017-08-01',      '2017-08-01',      '2017-08-01',      '2017-08-01',      '2017-08-01',      '2017-08-01',      '2017-08-01',      '2017-08-01',      '2017-09-01',      '2017-09-01',      '2017-09-01',      '2017-09-01',      '2017-09-01',      '2017-09-01',      '2017-09-01',      '2017-09-01',      '2017-09-01',      '2017-10-01',      '2017-10-01',      '2017-10-01',      '2017-10-01',      '2017-10-01',      '2017-10-01',      '2017-10-01',      '2017-10-01',      '2017-10-01',      '2017-11-01',      '2017-11-01',      '2017-11-01',      '2017-11-01',      '2017-11-01',      '2017-11-01',      '2017-11-01',      '2017-11-01',      '2017-11-01',      '2017-12-01',      '2017-12-01',      '2017-12-01',      '2017-12-01',      '2017-12-01',      '2017-12-01',      '2017-12-01',      '2017-12-01',      '2017-12-01',      '2018-01-01',      '2018-01-01',      '2018-01-01',      '2018-01-01',      '2018-01-01',      '2018-01-01',      '2018-01-01',      '2018-01-01',      '2018-01-01',      '2018-02-01',      '2018-02-01',      '2018-02-01',      '2018-02-01',      '2018-02-01',      '2018-02-01',      '2018-02-01',      '2018-02-01',      '2018-02-01',      '2018-03-01',      '2018-03-01',      '2018-03-01',      '2018-03-01',      '2018-03-01',      '2018-03-01',      '2018-03-01',      '2018-03-01',      '2018-03-01',      '2018-04-01',      '2018-04-01',      '2018-04-01',      '2018-04-01',      '2018-04-01',      '2018-04-01',      '2018-04-01',      '2018-04-01',      '2018-04-01',      '2018-05-01',      '2018-05-01',      '2018-05-01',      '2018-05-01',      '2018-05-01',      '2018-05-01',      '2018-05-01',      '2018-05-01',      '2018-05-01',      '2018-06-01',      '2018-06-01',      '2018-06-01',      '2018-06-01',      '2018-06-01',      '2018-06-01',      '2018-06-01',      '2018-06-01',      '2018-06-01',      '2018-07-01',      '2018-07-01',      '2018-07-01',      '2018-07-01',      '2018-07-01',      '2018-07-01',      '2018-07-01',      '2018-07-01',      '2018-07-01',      '2018-08-01',      '2018-08-01',      '2018-08-01',      '2018-08-01',      '2018-08-01',      '2018-08-01',      '2018-08-01',      '2018-08-01',      '2018-08-01',      '2018-09-01',      '2018-09-01',      '2018-09-01',      '2018-09-01',      '2018-09-01',      '2018-09-01',      '2018-09-01',      '2018-09-01',      '2018-09-01',      '2018-10-01',      '2018-10-01',      '2018-10-01',      '2018-10-01',      '2018-10-01',      '2018-10-01',      '2018-10-01',      '2018-10-01',      '2018-10-01',      '2018-11-01',      '2018-11-01',      '2018-11-01',      '2018-11-01',      '2018-11-01',      '2018-11-01',      '2018-11-01',      '2018-11-01',      '2018-11-01',      '2018-12-01',      '2018-12-01',      '2018-12-01',      '2018-12-01',      '2018-12-01',      '2018-12-01',      '2018-12-01',      '2018-12-01',      '2018-12-01',      '2019-01-01',      '2019-01-01',      '2019-01-01',      '2019-01-01',      '2019-01-01',      '2019-01-01',      '2019-01-01',      '2019-01-01',      '2019-01-01',      '2019-02-01',      '2019-02-01',      '2019-02-01',      '2019-02-01',      '2019-02-01',      '2019-02-01',      '2019-02-01',      '2019-02-01',      '2019-02-01',      '2019-03-01',      '2019-03-01',      '2019-03-01',      '2019-03-01',      '2019-03-01',      '2019-03-01',      '2019-03-01',      '2019-03-01',      '2019-03-01',      '2019-04-01',      '2019-04-01',      '2019-04-01',      '2019-04-01',      '2019-04-01',      '2019-04-01',      '2019-04-01',      '2019-04-01',      '2019-04-01',      '2019-05-01',      '2019-05-01',      '2019-05-01',      '2019-05-01',      '2019-05-01',      '2019-05-01',      '2019-05-01',      '2019-05-01',      '2019-05-01',      '2019-06-01',      '2019-06-01',      '2019-06-01',      '2019-06-01',      '2019-06-01',      '2019-06-01',      '2019-06-01',      '2019-06-01',      '2019-06-01',      '2019-07-01',      '2019-07-01',      '2019-07-01',      '2019-07-01',      '2019-07-01',      '2019-07-01',      '2019-07-01',      '2019-07-01',      '2019-07-01',      '2019-08-01',      '2019-08-01',      '2019-08-01',      '2019-08-01',      '2019-08-01',      '2019-08-01',      '2019-08-01',      '2019-08-01',      '2019-08-01',      '2019-09-01',      '2019-09-01',      '2019-09-01',      '2019-09-01',      '2019-09-01',      '2019-09-01',      '2019-09-01',      '2019-09-01',      '2019-09-01',      '2019-10-01',      '2019-10-01',      '2019-10-01',      '2019-10-01',      '2019-10-01',      '2019-10-01',      '2019-10-01',      '2019-10-01',      '2019-10-01',      '2019-11-01',      '2019-11-01',      '2019-11-01',      '2019-11-01',      '2019-11-01',      '2019-11-01',      '2019-11-01',      '2019-11-01',      '2019-11-01',      '2019-12-01',      '2019-12-01',      '2019-12-01',      '2019-12-01',      '2019-12-01',      '2019-12-01',      '2019-12-01',      '2019-12-01',      '2019-12-01',      '2020-01-01',      '2020-01-01',      '2020-01-01',      '2020-01-01',      '2020-01-01',      '2020-01-01',      '2020-01-01',      '2020-01-01',      '2020-01-01',      '2020-02-01',      '2020-02-01',      '2020-02-01',      '2020-02-01',      '2020-02-01',      '2020-02-01',      '2020-02-01',      '2020-02-01',      '2020-02-01',      '2020-03-01',      '2020-03-01',      '2020-03-01',      '2020-03-01',      '2020-03-01',      '2020-03-01',      '2020-03-01',      '2020-03-01',      '2020-03-01',      '2020-04-01',      '2020-04-01',      '2020-04-01',      '2020-04-01',      '2020-04-01',      '2020-04-01',      '2020-04-01',      '2020-04-01',      '2020-04-01',      '2020-05-01',      '2020-05-01',      '2020-05-01',      '2020-05-01',      '2020-05-01',      '2020-05-01',      '2020-05-01',      '2020-05-01',      '2020-05-01',      '2020-06-01',      '2020-06-01',      '2020-06-01',      '2020-06-01',      '2020-06-01',      '2020-06-01',      '2020-06-01',      '2020-06-01',      '2020-06-01',      '2020-07-01',      '2020-07-01',      '2020-07-01',      '2020-07-01',      '2020-07-01',      '2020-07-01',      '2020-07-01',      '2020-07-01',      '2020-07-01',      '2020-08-01',      '2020-08-01',      '2020-08-01',      '2020-08-01',      '2020-08-01',      '2020-08-01',      '2020-08-01',      '2020-08-01',      '2020-08-01',      '2020-09-01',      '2020-09-01',      '2020-09-01',      '2020-09-01',      '2020-09-01',      '2020-09-01',      '2020-09-01',      '2020-09-01',      '2020-09-01',      '2020-10-01',      '2020-10-01',      '2020-10-01',      '2020-10-01',      '2020-10-01',      '2020-10-01',      '2020-10-01',      '2020-10-01',      '2020-10-01',      '2020-11-01',      '2020-11-01',      '2020-11-01',      '2020-11-01',      '2020-11-01',      '2020-11-01',      '2020-11-01',      '2020-11-01',      '2020-11-01',      '2020-12-01',      '2020-12-01',      '2020-12-01',      '2020-12-01',      '2020-12-01',      '2020-12-01',      '2020-12-01',      '2020-12-01',      '2020-12-01',      '2021-01-01',      '2021-01-01',      '2021-01-01',      '2021-01-01',      '2021-01-01',      '2021-01-01',      '2021-01-01',      '2021-01-01',      '2021-01-01',      '2021-02-01',      '2021-02-01',      '2021-02-01',      '2021-02-01',      '2021-02-01',      '2021-02-01',      '2021-02-01',      '2021-02-01',      '2021-02-01',      '2021-03-01',      '2021-03-01',      '2021-03-01',      '2021-03-01',      '2021-03-01',      '2021-03-01',      '2021-03-01',      '2021-03-01',      '2021-03-01',      '2021-04-01',      '2021-04-01',      '2021-04-01',      '2021-04-01',      '2021-04-01',      '2021-04-01',      '2021-04-01',      '2021-04-01',      '2021-04-01',      '2021-05-01',      '2021-05-01',      '2021-05-01',      '2021-05-01',      '2021-05-01',      '2021-05-01',      '2021-05-01',      '2021-05-01',      '2021-05-01',      '2021-06-01',      '2021-06-01',      '2021-06-01',      '2021-06-01',      '2021-06-01',      '2021-06-01',      '2021-06-01',      '2021-06-01',      '2021-06-01',      '2021-07-01',      '2021-07-01',      '2021-07-01',      '2021-07-01',      '2021-07-01',      '2021-07-01',      '2021-07-01',      '2021-07-01',      '2021-07-01',      '2021-08-01',      '2021-08-01',      '2021-08-01',      '2021-08-01',      '2021-08-01',      '2021-08-01',      '2021-08-01',      '2021-08-01',      '2021-08-01',      '2021-09-01',      '2021-09-01',      '2021-09-01',      '2021-09-01',      '2021-09-01',      '2021-09-01',      '2021-09-01',      '2021-09-01',      '2021-09-01',      '2021-10-01',      '2021-10-01',      '2021-10-01',      '2021-10-01',      '2021-10-01',      '2021-10-01',      '2021-10-01',      '2021-10-01',      '2021-10-01',      '2021-11-01',      '2021-11-01',      '2021-11-01',      '2021-11-01',      '2021-11-01',      '2021-11-01',      '2021-11-01',      '2021-11-01',      '2021-11-01',      '2021-12-01',      '2021-12-01',      '2021-12-01',      '2021-12-01',      '2021-12-01',      '2021-12-01',      '2021-12-01',      '2021-12-01',      '2021-12-01',      '2022-01-01',      '2022-01-01',      '2022-01-01',      '2022-01-01',      '2022-01-01',      '2022-01-01',      '2022-01-01',      '2022-01-01',      '2022-01-01',      '2022-02-01',      '2022-02-01',      '2022-02-01',      '2022-02-01',      '2022-02-01',      '2022-02-01',      '2022-02-01',      '2022-02-01',      '2022-02-01',      '2022-03-01',      '2022-03-01',      '2022-03-01',      '2022-03-01',      '2022-03-01',      '2022-03-01',      '2022-03-01',      '2022-03-01',      '2022-03-01',      '2022-04-01',      '2022-04-01',      '2022-04-01',      '2022-04-01',      '2022-04-01',      '2022-04-01',      '2022-04-01',      '2022-04-01',      '2022-04-01'    ]  },  {    'name': '与上一月差异百分比',    'data': [      0,      0,      0,      0,      0,      0,      0,      0,      0,      0.001828,      -0.006677,      0.009749,      0.002228,      0.008848,      -0.00237,      0.017156,      0.006563,      0.000226,      0.003657,      -0.004739,      0.034312,      0.000453,      0.004456,      -0.013355,      0.013127,      0.017696,      0.019499,      0.026544,      0.000679,      0.01969,      0.005485,      -0.020032,      0.029248,      -0.007109,      0.006684,      0.051468,      0.008912,      0.068624,      0.026254,      0.038998,      0.007314,      -0.02671,      0.035393,      -0.009479,      0.000906,      0.048747,      -0.033387,      0.001132,      0.01114,      0.009142,      0.032817,      0.044241,      -0.011849,      0.085779,      0.045931,      0.001383,      0.053896,      0.022471,      0.005955,      -0.045245,      0.100599,      -0.018678,      0.035145,      0.051672,      0.049667,      -0.02396,      0.021599,      0.055585,      0.119888,      0.003384,      -0.048708,      0.013536,      0.018771,      0.138985,      0.019838,      -0.055042,      0.065671,      0.000278,      -0.023352,      0.052315,      0.077206,      0.022393,      0.016765,      -0.059688,      0.054062,      0.102003,      0.090587,      -0.022378,      0.15535,      0.000999,      0.063498,      -0.063498,      0.016149,      0.097881,      -0.020425,      0.170912,      -0.000123,      0.118466,      0.02319,      -0.000211,      0.192739,      -0.067708,      0.071221,      0.01708,      0.102501,      0.022165,      0.124487,      -0.019143,      -0.018125,      -0.001126,      0.121083,      0.020196,      0.017988,      0.075097,      0.108996,      0.211133,      -0.074198,      0.018783,      0.122153,      -0.083544,      0.000259,      0.023917,      0.116706,      0.220813,      -0.019673,      0.079911,      0.234656,      0.086448,      0.020015,      0.124086,      -0.027386,      0.031676,      0.00581,      0.13518,      -0.093549,      0.132826,      0.093216,      -0.104238,      0.141377,      0.006592,      0.034912,      0.250072,      -0.035774,      0.026802,      0.273045,      0.105072,      0.005788,      -0.044657,      0.138598,      0.040229,      0.150574,      -0.110599,      0.033013,      0.00365,      0.031017,      0.127312,      0.160868,      -0.053474,      -0.115466,      0.140931,      0.29932,      0.045905,      0.150523,      0.001448,      -0.122967,      0.169855,      0.0496,      0.316985,      0.143343,      -0.063992,      0.031282,      -0.132543,      0.037175,      0.191671,      0.007501,      0.133422,      0.164692,      -0.077929,      0.058259,      0.330603,      0.066029,      -0.145123,      0.224978,      0.184799,      0.037572,      0.012821,      0.119747,      -0.094321,      0.345993,      -0.159684,      0.071391,      0.362814,      0.014606,      0.111112,      0.037013,      -0.112787,      0.203814,      0.259799,      0.383412,      0.019109,      0.040742,      -0.173319,      0.110131,      0.262506,      0.21785,      0.076389,      -0.133827,      -0.156074,      0.079278,      0.023229,      0.244903,      0.249421,      -0.182941,      0.042081,      0.413066,      0.129968,      -0.190333,      0.276493,      0.048422,      0.241813,      0.14072,      0.440566,      0.030293,      -0.177402,      0.083491,      0.137597,      0.460609,      0.037566,      0.060652,      0.086282,      -0.19665,      0.248066,      -0.196046,      0.293545,      -0.212275,      -0.20136,      0.042281,      0.073021,      0.309231,      0.085384,      0.262092,      0.124991,      0.474717,      0.482411,      0.076262,      0.094322,      -0.223331,      -0.209045,      0.315988,      0.272079,      0.05392,      0.088311,      -0.215925,      0.071886,      0.280071,      0.498431,      0.061205,      -0.234372,      0.341568,      0.088057,      0.079859,      0.287663,      0.07978,      0.512778,      0.083828,      -0.219912,      0.065199,      0.080235,      0.359355,      -0.245897,      0.524452,      0.359876,      0.084965,      0.077889,      0.296056,      -0.255712,      0.080213,      -0.222735,      0.08588,      0.363086,      0.081994,      0.089747,      -0.223835,      0.548114,      0.081618,      0.070501,      -0.258912,      0.298711,      -0.224263,      0.077948,      0.076036,      0.369433,      0.041687,      -0.25718,      0.096203,      0.281945,      0.573601,      0.091646,      0.390335,      0.014868,      0.23632,      -0.221567,      0.077943,      0.067484,      -0.258709,      0.597885,      0.083916,      0.402905,      0.616232,      0.078835,      0.227049,      -0.007913,      -0.218931,      0.048852,      -0.261094,      -0.030661,      0.071893,      0.414289,      0.028432,      0.245945,      0.632236,      -0.220757,      0.077534,      -0.263585,      -0.047725,      -0.222185,      0.012736,      -0.265072,      0.078744,      0.650231,      0.055903,      0.235779,      0.432364,      -0.265748,      0.04274,      -0.220451,      -0.057594,      0.667164,      0.434204,      0.219965,      0.085224,      -0.008774,      -0.26679,      0.09454,      -0.064687,      0.433422,      -0.217534,      0.032651,      -0.028401,      0.680927,      0.214461,      -0.037727,      -0.21023,      0.105339,      0.026199,      0.693708,      -0.267256,      0.439321,      -0.072231,      0.221864,      -0.078034,      0.229284,      0.024888,      -0.075021,      -0.26684,      0.460747,      0.114086,      -0.20315,      0.706848,      0.234251,      -0.080129,      -0.125348,      0.123692,      -0.267966,      0.715462,      0.026688,      0.482541,      -0.201062,      -0.200223,      0.716276,      0.233125,      -0.163999,      -0.09001,      0.480199,      0.130081,      -0.269488,      0.036764,      0.037838,      0.246154,      -0.082232,      -0.275613,      0.485894,      0.707751,      -0.195233,      0.138289,      -0.20055,      0.149501,      0.278285,      -0.288326,      0.027657,      -0.050805,      -0.202664,      0.487514,      -0.222162,      0.692973,      -0.297809,      0.027627,      0.678128,      0.161133,      0.477925,      -0.016817,      0.31056,      -0.207638,      -0.24371,      0.669888,      -0.210058,      0.030544,      -0.302273,      0.47368,      -0.247484,      0.17721,      0.326968,      0.015835,      0.030346,      0.467203,      0.661813,      0.330424,      -0.308722,      -0.273886,      0.191665,      0.045536,      -0.212298,      0.457512,      0.03259,      -0.317515,      0.655968,      0.350941,      0.069042,      -0.216715,      -0.305993,      0.207767,      -0.326043,      -0.21995,      0.656549,      0.083236,      0.037922,      -0.314572,      0.362885,      0.225878,      0.463372,      0.361364,      0.241977,      0.043192,      -0.314054,      -0.332888,      0.468988,      0.659842,      0.094592,      -0.221972,      0.102728,      -0.224009,      -0.339211,      0.466353,      0.052746,      0.366538,      0.257353,      -0.30822,      0.66411,      -0.234342,      0.656293,      0.439568,      0.254732,      -0.351202,      -0.315375,      0.058751,      0.368324,      0.103069,      0.064215,      0.650718,      0.412346,      -0.243828,      -0.361927,      0.248318,      0.103034,      -0.32161,      0.370869,      0.41426,      -0.367714,      0.069662,      0.657675,      -0.315546,      -0.247563,      0.37706,      0.255172,      0.108842,      -0.375101,      0.080199,      0.109715,      0.412075,      -0.312511,      0.662878,      0.375828,      0.262926,      -0.251283,      0.268663,      -0.251464,      0.674547,      0.106521,      0.096067,      0.377392,      -0.309493,      0.411107,      -0.382021,      0.384415,      0.414527,      0.104527,      -0.386496,      -0.304469,      0.693328,      0.275553,      -0.246403,      0.100363,      0.096703,      -0.342466,      0.399143,      0.300296,      0.658323,      -0.398813,      0.152698,      -0.262013,      0.395636,      0.309749,      0.404816,      -0.348323,      0.162776,      0.657784,      0.102665,      -0.266491,      0.388783,      -0.40685,      -0.270969,      0.657245,      0.319202,      0.108627,      0.410489,      -0.35418,      0.172854,      -0.414887,      0.38193,      0.656706,      -0.422924,      0.416162,      -0.275447,      0.375077,      0.182933,      0.114589,      -0.360037,      0.328655,      -0.279925,      0.193011,      -0.430961,      -0.365894,      0.656167,      0.338108,      0.421835,      0.120551,      0.368224,      0.361371,      0.203089,      0.655628,      0.126513,      -0.371752,      0.347561,      -0.284403,      0.427508,      -0.438999    ]  },  {    'name': '宽度',    'data': [      0,      0,      0,      0,      0,      0,      0,      0,      0,      0.049702,      0.073147,      0.021422,      0.048137,      0.00547,      0.177735,      0.096527,      0.03286,      0.030852,      0.061471,      0.178447,      0.096412,      0.028122,      0.044115,      0.072277,      0.031944,      0.005771,      0.036841,      0.004884,      0.027858,      0.025579,      0.074296,      0.070558,      0.069119,      0.183378,      0.044447,      0.086983,      0.044954,      0.081206,      0.024961,      0.091777,      0.080323,      0.07022,      0.004593,      0.180183,      0.028436,      0.068086,      0.071354,      0.031062,      0.04959,      0.06937,      0.02743,      0.005461,      0.194331,      0.089,      0.025975,      0.078168,      0.070115,      0.0528,      0.02919,      0.072485,      0.086619,      0.202842,      0.004817,      0.028334,      0.035275,      0.212369,      0.054926,      0.005385,      0.097277,      0.02901,      0.0698,      0.062934,      0.054408,      0.100989,      0.062226,      0.069152,      0.018769,      0.030529,      0.199413,      0.028816,      0.005767,      0.078459,      0.0502,      0.066053,      0.029118,      0.00919,      0.005535,      0.180453,      0.093809,      0.029438,      0.032157,      0.061664,      0.042406,      0.008344,      0.172315,      0.111372,      0.028758,      0.007989,      0.047091,      0.029392,      0.104935,      0.063534,      0.033577,      0.044539,      0.006548,      0.074643,      0.008299,      0.167892,      0.16812,      0.028993,      0.010287,      0.059786,      0.044459,      0.033823,      0.006903,      0.115521,      0.066159,      0.056906,      0.017712,      0.06738,      0.028011,      0.04562,      0.007134,      0.118796,      0.175435,      0.034459,      0.114106,      0.030779,      0.064242,      0.006987,      0.183512,      0.043455,      0.028794,      0.05413,      0.067077,      0.00545,      0.027924,      0.067713,      0.082659,      0.027689,      0.042853,      0.103536,      0.182737,      0.07557,      0.09722,      0.028993,      0.027822,      0.189692,      0.004998,      0.045856,      0.092206,      0.066468,      0.080311,      0.031615,      0.067761,      0.028617,      0.072767,      0.190644,      0.067819,      0.005671,      0.118662,      0.049734,      0.027408,      0.027998,      0.064785,      0.062546,      0.053676,      0.09545,      0.005472,      0.211157,      0.07666,      0.061317,      0.062206,      0.043752,      0.03117,      0.005756,      0.030155,      0.197022,      0.06071,      0.108704,      0.057339,      0.060313,      0.021345,      0.031413,      0.064905,      0.032245,      0.006378,      0.179732,      0.11853,      0.056386,      0.050904,      0.108529,      0.028268,      0.006087,      0.086524,      0.162041,      0.031095,      0.011872,      0.144726,      0.029366,      0.048857,      0.060318,      0.00856,      0.01311,      0.038192,      0.047674,      0.150387,      0.149775,      0.045647,      0.027292,      0.011914,      0.042573,      0.056455,      0.069717,      0.126566,      0.006642,      0.060251,      0.040514,      0.065131,      0.014297,      0.00713,      0.130857,      0.029555,      0.138472,      0.047501,      0.005585,      0.131956,      0.031675,      0.060888,      0.052304,      0.141788,      0.032212,      0.05671,      0.037344,      0.142716,      0.055966,      0.028844,      0.060887,      0.041484,      0.045498,      0.06789,      0.006714,      0.133153,      0.119692,      0.078041,      0.004573,      0.139666,      0.053614,      0.03007,      0.098444,      0.028885,      0.046833,      0.056959,      0.08374,      0.078718,      0.124169,      0.029769,      0.137211,      0.036334,      0.04749,      0.005745,      0.071319,      0.007567,      0.153771,      0.051344,      0.060668,      0.032544,      0.06645,      0.042614,      0.142092,      0.11789,      0.031708,      0.056797,      0.032,      0.061372,      0.162849,      0.005009,      0.059137,      0.088008,      0.037379,      0.060723,      0.070215,      0.057074,      0.128206,      0.005804,      0.032245,      0.157914,      0.050232,      0.055355,      0.056317,      0.004651,      0.03479,      0.034461,      0.147882,      0.076472,      0.026766,      0.128554,      0.080086,      0.032284,      0.034176,      0.014925,      0.048039,      0.055213,      0.003729,      0.143679,      0.114606,      0.04837,      0.054291,      0.172173,      0.04308,      0.016837,      0.028544,      0.056752,      0.008992,      0.118608,      0.030902,      0.083419,      0.038496,      0.006199,      0.014629,      0.129142,      0.053118,      0.04549,      0.129215,      0.034803,      0.058701,      0.00763,      0.114458,      0.048934,      0.152712,      0.060363,      0.018704,      0.044944,      0.151266,      0.071775,      0.056356,      0.021269,      0.159617,      0.035084,      0.030058,      0.047534,      0.005272,      0.141106,      0.045725,      0.019135,      0.048033,      0.055579,      0.060219,      0.006284,      0.158829,      0.055818,      0.003864,      0.058668,      0.046601,      0.071496,      0.144835,      0.134328,      0.037647,      0.019872,      0.071804,      0.022879,      0.097491,      0.078051,      0.003972,      0.131719,      0.037252,      0.048723,      0.056468,      0.13742,      0.068578,      0.023553,      0.00662,      0.04865,      0.136273,      0.17401,      0.05311,      0.049388,      0.057999,      0.059333,      0.135308,      0.055487,      0.003865,      0.029079,      0.037149,      0.06085,      0.161913,      0.076041,      0.061348,      0.0427,      0.028276,      0.15578,      0.033182,      0.146223,      0.004082,      0.063948,      0.059881,      0.063555,      0.029628,      0.145282,      0.069372,      0.033479,      0.057598,      0.038458,      0.003759,      0.139338,      0.144166,      0.076973,      0.130569,      0.060103,      0.032603,      0.029892,      0.018344,      0.058441,      0.003379,      0.183708,      0.058608,      0.049407,      0.120009,      0.068452,      0.004373,      0.048016,      0.019714,      0.028777,      0.085231,      0.039063,      0.135634,      0.015966,      0.122697,      0.004114,      0.051344,      0.029206,      0.054877,      0.042793,      0.074497,      0.114066,      0.147924,      0.016412,      0.029601,      0.058395,      0.004567,      0.050256,      0.123841,      0.056094,      0.146565,      0.031902,      0.059342,      0.004174,      0.043405,      0.055432,      0.041083,      0.069734,      0.05042,      0.056539,      0.003794,      0.110794,      0.043129,      0.140956,      0.030441,      0.052182,      0.032292,      0.053453,      0.121577,      0.036071,      0.075129,      0.085273,      0.05497,      0.003483,      0.131642,      0.057493,      0.133368,      0.035959,      0.05841,      0.124196,      0.00385,      0.079035,      0.097945,      0.033252,      0.051811,      0.161163,      0.046305,      0.053095,      0.114508,      0.055199,      0.033914,      0.003225,      0.071057,      0.033683,      0.143742,      0.08089,      0.135922,      0.002983,      0.05659,      0.070416,      0.072386,      0.035128,      0.135224,      0.064939,      0.032136,      0.040608,      0.003821,      0.146826,      0.037904,      0.072754,      0.057024,      0.072831,      0.056953,      0.145628,      0.037545,      0.074121,      0.033133,      0.004088,      0.034828,      0.13475,      0.01923,      0.034453,      0.087347,      0.125984,      0.003826,      0.13321,      0.068097,      0.05556,      0.031505,      0.048538,      0.002973,      0.020345,      0.037079,      0.164706,      0.083733,      0.027401,      0.043598,      0.0485,      0.055077,      0.017495,      0.004683,      0.030558,      0.14297,      0.094748,      0.053463,      0.040617,      0.110267,      0.053343,      0.155145,      0.054222,      0.0736,      0.020136,      0.004805,      0.032303,      0.100214,      0.042533,      0.150233,      0.104146,      0.038636,      0.054706,      0.039867,      0.029808,      0.076916,      0.004341,      0.060174,      0.053257,      0.030305,      0.099061,      0.00403,      0.161707,      0.052122,      0.075831,      0.064079,      0.043691,      0.037821,      0.028129,      0.150195,      0.080979,      0.003917,      0.061007,      0.06114,      0.085135,      0.112985    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  dim: 0, // 维度（一个）  compare: 1, // 对比（一个）  values: [2, 3], // 数值（多个）  scale: 50, // 宽度倍数  legend: true, // 显示图例  sort: true, // 弹窗排序  seriesNum: 100, // 弹窗显示数量  right: '10%' // 右边距，百分比或数值}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar seriesData = {}// {dim: [values1_name: {name: '', data: [{name: compare[i], value: values1[i]}]},// values2_name: {name: '', data: [{name: compare[i], value: values2[i]}]}]}for (let j = 0; j < rowNum; j++) {  let dim = data[config.dim].data[j]  if (dim in seriesData) {    config.values.forEach((item, index) => {      seriesData[dim][index].data.push({ 'name': data[config.compare].data[j], 'value': data[item].data[j] })    })  }  else {    seriesData[dim] = config.values.map((item, index) => {      return { 'name': data[item].name, 'data': [{ 'name': data[config.compare].data[j], 'value': data[item].data[j] }] }    })  }}var option = {  tooltip: {    confine: true,    trigger: 'axis',    formatter: function (params) {      if (config.sort) { params.sort((a, b) => { return b.value - a.value }) }      if (config.seriesNum) { params = params.slice(0, config.seriesNum) }      let res = params[0].axisValue + '<br>'      params.forEach(item => {        res += item.marker + item.seriesName + '<div style="float:right">' + '&nbsp;&nbsp;&nbsp;&nbsp;' + item.value + '%&nbsp;&nbsp;&nbsp;&nbsp;占比：' + (item.data.weight * 100 / config.scale).toFixed(1) + '%</div><br>'      })      return res    }  },  grid: {    right: config.right  },  legend: {    show: config.legend,    data: []  },  xAxis: {    type: 'category',    data: Array.from(new Set(data[config.compare].data)),    axisLabel: {      rotate: 30    }  },  yAxis: {    //min: -100,    //max: 100,    type: 'value',    axisLabel: {      formatter: '{value}%'    }  },  series: []}Object.keys(seriesData).forEach((item, index) => {  option.legend.data.push(item)  let list1 = [] // 数值  let list2 = [] // 下限  let list3 = [] // 宽度  for (let j = 0; j < seriesData[item][0].data.length; j++) {    let date = seriesData[item][0].data[j].name    let value = seriesData[item][0].data[j].value * 100    let weight = seriesData[item][1].data[j].value * config.scale    let width = weight / 2    list1.push({ 'date': date, 'value': value.toFixed(2), 'weight': weight })    list2.push({ 'date': date, 'value': value - (value < 0 ? (width.toFixed(2) > 0 ? -width.toFixed(2) : 0) : width.toFixed(2)) })    list3.push({ 'date': date, 'value': value < 0 ? (weight.toFixed(2) > 0 ? -weight.toFixed(2) : 0) : weight.toFixed(2) })  }  option.series.push({    name: item,    type: 'line',    data: list1,    smooth: true,    itemStyle: {      color: config.colors[index % config.colors.length],    },    endLabel: {      show: true,      formatter: '{a}'    },    showSymbol: false,    emphasis: {      focus: 'series'    },  })  option.series.push({    name: item,    tooltip: {      trigger: 'none',    },    type: 'line',    smooth: true,    data: list2,    lineStyle: {      opacity: 0    },    stack: index + '',    showSymbol: false,    emphasis: {      focus: 'series'    },  });  option.series.push({    name: item,    tooltip: {      trigger: 'none',    },    type: 'line',    smooth: true,    data: list3,    lineStyle: {      opacity: 0    },    areaStyle: {      color: config.colors[index % config.colors.length],      opacity: 1    },    stack: index + '',    showSymbol: false,    emphasis: {      focus: 'series'    },  })})chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/364f4c67.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 流程图</title>
      <link>https://blog.eurkon.com/post/b7fd4932.html</link>
      <guid>https://blog.eurkon.com/post/b7fd4932.html</guid>
      <pubDate>Thu, 05 May 2022 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 640px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 640px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': '需求预测',    'data': [      4408124    ]  },  {    'name': '生产计划',    'data': [      4827844.65    ]  },  {    'name': '质检入仓',    'data': [      4697866    ]  },  {    'name': '实际销售',    'data': [      3955159    ]  },  {    'name': '成品库存',    'data': [      1244739    ]  },  {    'name': '订单缺货',    'data': [      1078888    ]  },  {    'name': '包材库存',    'data': [      20050540.15    ]  },  {    'name': '采购订单',    'data': [      112094545.63    ]  },  {    'name': '采购交货',    'data': [      20371651.4    ]  },  {    'name': '包材在途',    'data': [      61087618.59    ]  },  {    'name': '销售交货',    'data': [      334966109    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#4379CE', '#D4E1F7', '#FC8602', '#009688', '#E6122F', '#72B5EB', '#78CDED', '#AC9AE2', '#FAC36F', '#FD7F7F']}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthfunction numberFormat (value) {  let k = 10000  let sizes = ['', '万', '亿', '万亿']  if (value >= k) {    let i = Math.floor(Math.log(value) / Math.log(k));    return (value / Math.pow(k, i)).toFixed(2) + sizes[i];  }  return Number(value).toFixed(2);}var dataMap = {}for (let i = 0; i < colNum; i++) {  dataMap[data[i].name] = numberFormat(data[i].data[0])}var width = 1000var height = 1000var option = {  series: [    {      type: 'graph',      top: 1,      bottom: 50,      left: 100,      right: 50,      label: {        show: true,        fontSize: 16,        fontWeight: 500,        formatter: function (params) {          if (params.value === 0 || params.value) {            return '{name|' + params.name + '}\n{value|' + (params.value || 0) + '}'          }          if (params.data.type) {            return '{' + params.data.type + '|' + params.name + '}'          }          return params.name        },        rich: {          roundRect: {            height: 50,            width: 100,            fontSize: 16,            fontWeight: 500,            color: '#FFF',            align: 'center',            backgroundColor: config.colors[2],            borderRadius: 10,          },          name: {            height: 35,            width: 100,            fontSize: 16,            fontWeight: 500,            color: '#FFF',            align: 'center',            backgroundColor: config.colors[0]          },          value: {            height: 35,            width: 100,            fontSize: 14,            fontWeight: 550,            color: config.colors[0],            align: 'center',            backgroundColor: config.colors[1]          }        }      },      emphasis: { disabled: true },      edgeSymbol: ['none', 'arrow'],      symbolSize: 100,      edgeLabel: { fontSize: 25, },      itemStyle: { color: 'rgba(0,0,0,0)' },      lineStyle: { opacity: 1, width: 2, color: config.colors[0] },      markLine: {        lineStyle: { opacity: 1, width: 2, color: config.colors[0], type: 'solid' },        label: {          show: true,          fontSize: 16,          position: 'middle',          fontWeight: 500,        },        emphasis: { disabled: true },        symbol: ['none', 'none'],        data: [          [{ name: '计划', x: 1, y: 0, lineStyle: { color: config.colors[2] } }, { x: 1, y: '35%', }],          [{ x: 1, y: '35%', lineStyle: { color: config.colors[1] } }, { x: '100%', y: '35%', }],          [{ name: '采购', x: 1, y: '35%', lineStyle: { color: config.colors[3] } }, { x: 1, y: '57%', }],          [{ x: 1, y: '57%', lineStyle: { color: config.colors[1] } }, { x: '100%', y: '57%', }],          [{ name: '生产', x: 1, y: '57%', lineStyle: { color: config.colors[4] } }, { x: 1, y: '80%', }],          [{ x: 1, y: '80%', lineStyle: { color: config.colors[1] } }, { x: '100%', y: '80%', }],          [{ name: '销售', x: 1, y: '80%', lineStyle: { color: config.colors[5] } }, { x: 1, y: '100%', }]        ],      },      data: [        {          name: '开始',          x: 0 * width,          y: 0 * height,          type: 'roundRect',        },        {          name: '需求预测',          value: dataMap['需求预测'],          x: 1 * width,          y: 0 * height,        },        {          name: '需求预测1',          x: 1 * width,          y: 0 * height,          symbolSize: 70,          label: { show: false },        },        {          name: '是否生产',          x: 2 * width,          y: 0 * height,          symbol: 'diamond',          symbolSize: 90,          itemStyle: {            color: config.colors[3]          }        },        {          name: '生产计划',          value: dataMap['生产计划'],          x: 4 * width,          y: 0 * height,        },        {          name: '生1',          x: 4 * width,          y: .6 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '生2',          x: 1 * width,          y: .6 * height,          symbolSize: 0,          label: { show: false },        },        {          name: 'BOM表',          x: 1 * width,          y: 1.25 * height,          symbol: 'rect',          symbolSize: 70,          itemStyle: { color: config.colors[4] }        },        {          name: '采购订单',          value: dataMap['采购订单'],          x: 2 * width,          y: 1.25 * height,        },        {          name: '采购交货',          value: dataMap['采购交货'],          x: 3 * width,          y: 1.25 * height,        },        {          name: '交1',          x: 3 * width,          y: 1.6 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '交2',          x: 1 * width,          y: 1.6 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '包材在途',          value: dataMap['包材在途'],          x: 4 * width,          y: 1.25 * height,        },        {          name: '途1',          x: 4.75 * width,          y: 1.25 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '途2',          x: 4.75 * width,          y: -0.5 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '包材库存',          value: dataMap['包材库存'],          x: 1 * width,          y: 2.1 * height,        },        {          name: '包材库存1',          x: 1 * width,          y: 2.1 * height,          symbolSize: 70,          label: { show: false },        },        {          name: '包1',          x: 1 * width,          y: 2.5 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '包2',          x: 5.25 * width,          y: 2.5 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '包3',          x: 5.25 * width,          y: -0.5 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '包4',          x: 2 * width,          y: -0.5 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '生产',          x: 2 * width,          y: 2.1 * height,          symbol: 'rect',          symbolSize: 70,          itemStyle: {            color: config.colors[4]          }        },        {          name: '出检入仓',          value: dataMap['质检入仓'],          x: 3 * width,          y: 2.1 * height,        },        {          name: '成品库存',          value: dataMap['成品库存'],          x: 4 * width,          y: 2.1 * height,        },        {          name: '成1',          x: 5 * width,          y: 2.1 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '成2',          x: 5 * width,          y: -0.5 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '成3',          x: 2 * width,          y: -0.5 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '实际销售',          value: dataMap['实际销售'],          x: 4 * width,          y: 3 * height,        },        {          name: '实际销售1',          x: 4 * width,          y: 3 * height,          symbolSize: 70,          label: { show: false },        },        {          name: '结束',          x: 2 * width,          y: 3 * height,          type: 'roundRect',        },        {          name: '销售交货',          value: dataMap['销售交货'],          x: 3 * width,          y: 3 * height,        },        {          name: '订单缺货',          value: dataMap['订单缺货'],          x: 5 * width,          y: 3 * height,        },        {          name: '订1',          x: 5.5 * width,          y: 3 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '订2',          x: 5.5 * width,          y: -0.6 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '订3',          x: 1 * width,          y: -0.6 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '否1',          x: 2 * width,          y: .5 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '否2',          x: .3 * width,          y: .5 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '否3',          x: .3 * width,          y: 2.7 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '否4',          x: 2.4 * width,          y: 2.7 * height,          symbolSize: 0,          label: { show: false },        },        {          name: '否5',          x: 2.4 * width,          y: 2.1 * height,          symbolSize: 0,          label: { show: false },        },      ],      links: [        {          source: '开始',          target: '需求预测',        },        {          source: '需求预测',          target: '是否生产',        },        {          source: '是否生产',          target: '生产计划',          label: { //线上面显示内容            show: true,            align: 'left',            position: 'start',            formatter: '                        是',            fontSize: 16,          },        },        {          source: 'BOM表',          target: '采购订单',        },        {          source: '采购订单',          target: '采购交货',        },        {          source: '采购交货',          target: '包材在途',          lineStyle: { type: 'dashed' },        },        {          source: '包材库存',          target: '生产',        },        {          source: '生产',          target: '出检入仓',        },        {          source: '出检入仓',          target: '成品库存',        },        {          source: '成品库存',          target: '实际销售1',        },        {          source: '实际销售',          target: '销售交货',        },        {          source: '销售交货',          target: '结束',        },        {          source: '实际销售',          target: '订单缺货',          lineStyle: { type: 'dashed' },        },        {          source: '是否生产',          target: '否1',          symbol: 'none',        },        {          label: { //线上面显示内容            show: true,            align: 'right',            position: 'start',            formatter: '否                ',            fontSize: 16,          },          source: '否1',          target: '否2',          symbol: 'none',        },        {          source: '否2',          target: '否3',          symbol: 'none',        },        {          source: '否3',          target: '否4',          symbol: 'none',        },        {          source: '否4',          target: '否5',          symbol: ['none', 'none'],        },        {          source: '生产计划',          target: '生1',          symbol: 'none',        },        {          source: '生1',          target: '生2',          symbol: 'none',        },        {          source: '生2',          target: 'BOM表',          symbol: ['none', 'arrow'],        },        {          source: '采购交货',          target: '交1',          symbol: 'none',        },        {          source: '交1',          target: '交2',          symbol: 'none',        },        {          source: '交2',          target: '包材库存1',          symbol: ['none', 'arrow'],        },        {          source: '包材库存',          target: '包1',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '包1',          target: '包2',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '包2',          target: '包3',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '包3',          target: '包4',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '包4',          target: '是否生产',          symbol: ['none', 'arrow'],          lineStyle: { type: 'dashed' },        },        {          source: '成品库存',          target: '成1',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '成1',          target: '成2',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '成2',          target: '成3',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '成3',          target: '成4',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '成4',          target: '是否生产',          symbol: ['none', 'arrow'],          lineStyle: { type: 'dashed' },        },        {          source: '订单缺货',          target: '订1',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '订1',          target: '订2',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '订2',          target: '订3',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '订3',          target: '需求预测1',          symbol: ['none', 'arrow'],          lineStyle: { type: 'dashed' },        },        {          source: '包材在途',          target: '途1',          symbol: 'none',          lineStyle: { type: 'dashed' },        },        {          source: '途1',          target: '途2',          symbol: 'none',          lineStyle: { type: 'dashed' },        },      ],    },  ],};chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/b7fd4932.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>库存周转分析</title>
      <link>https://blog.eurkon.com/post/e305ad56.html</link>
      <guid>https://blog.eurkon.com/post/e305ad56.html</guid>
      <pubDate>Fri, 01 Apr 2022 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;h3 id=&quot;模型概念&quot;&gt;&lt;a href=&quot;#模型概念&quot; class=&quot;headerlink&quot; title=&quot;模型概念&quot;&gt;&lt;/a&gt;模型概念&lt;/h</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><h3 id="模型概念"><a href="#模型概念" class="headerlink" title="模型概念"></a>模型概念</h3><p>库存管理一直是企业管理的重要组成部分，在企业生产经营活动中，库存管理的好坏将影响到企业的购、销活动等，通过对企业的库存数据进行分析，有利于及时发现库存管理中存在的问题并及时地采取某些应对策略，以减少企业库存的资金占用、管理成本等。</p><ul><li><strong>平均存货余额</strong>：通常是由物流设施中储备的材料、零部件、在制品和产成品构成。</li><li><strong>销货成本</strong>：企业当期已售商品之成本，即为制造这些产品所直接投入的原材料、劳动力及分摊的制造费用。 </li><li><strong>库存周转率</strong>：企业在一定时期销货成本与平均存货余额的比率，用于反映库存周转快慢程度。周转率越高表明存货周转速度越快，从成本到商品销售到资金回流的周期越短，销售情况越好。</li><li><strong>库存周转天数</strong>：企业从取得存货开始，至消耗、销售为止所经历的天数。周转天数越少，说明存货变现速度越快，销售状况越良好。</li></ul><div class="table-container"><table><thead><tr><th>数据指标</th><th>计算公式</th></tr></thead><tbody><tr><td>平均存货余额</td><td>(期初存货金额 + 期末存货金额) / 2<br>期初存货金额：上期账户结转至本期账户的余额，在数额上等于上期期末金额<br>期末存货金额 = 期初金额 + 本期增加发生额 - 本期减少发生额</td></tr><tr><td>销货成本</td><td>单件销货成本 * 销售件数</td></tr><tr><td>库存周转率</td><td>销货成本 / 平均存货余额</td></tr><tr><td>库存周转天数</td><td>360 / 库存周转率</td></tr></tbody></table></div><h3 id="应用意义"><a href="#应用意义" class="headerlink" title="应用意义"></a>应用意义</h3><p>通过对库存周转率的分析，可以测定企业在一定时期内库存的周转速度。一般来说，库存周转率越高，表明库存周转越快，库存及在库存上占用的资金周转速度越快，企业盈利的机会更大，企业库存资产的变现能力越强，但具体分析应结合企业自身情况。不同行业的平均存货周转率是不同的，企业需与自身所处行业的平均水平进行对比分析，如果自身周转率低于行业水平，那么便需要尽快找到原因并及时改善。</p><h2 id="实战"><a href="#实战" class="headerlink" title="实战"></a>实战</h2><h3 id="基础数据"><a href="#基础数据" class="headerlink" title="基础数据"></a>基础数据</h3><p>这里选择 2016 年 1 月到 2017 年 7 月的销售和库存数据集进行分析，数据字段为：</p><ul><li>month：月份</li><li>warehouse：仓库</li><li>product_code：产品编码</li><li>product_name：产品名称</li><li>category：产品类别</li><li>sale_num：销售数量</li><li>inventory：库存</li><li>open_inv_amount：期初库存金额</li><li>end_inv_amount：期末库存金额</li><li>cost：销货成本</li><li>tax：销货税额</li></ul><p>部分数据如下：</p><div class="table-container"><table><thead><tr><th>month</th><th>warehouse</th><th>product_code</th><th>product_name</th><th>category</th><th>sale_num</th><th>inventory</th><th>open_inv_amount</th><th>end_inv_amount</th><th>cost</th><th>tax</th></tr></thead><tbody><tr><td>2016-01</td><td>2-零货库</td><td>08.03.0490</td><td>感冒清热颗粒</td><td>1-中成药</td><td>0</td><td>0</td><td>72</td><td>0</td><td>72</td><td>12</td></tr><tr><td>2016-01</td><td>1-立体仓库</td><td>13.333</td><td>连花清瘟胶囊</td><td>1-中成药</td><td>4</td><td>1665</td><td>7266</td><td>14998</td><td>9142</td><td>1595</td></tr><tr><td>2016-01</td><td>1-立体仓库</td><td>08.02.0917</td><td>氢溴酸东莨菪碱注射液</td><td>2-化学药制剂</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr><tr><td>2016-01</td><td>2-零货库</td><td>13.449</td><td>盐酸二甲双胍片</td><td>2-化学药制剂</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr><tr><td>2016-01</td><td>2-零货库</td><td>13.1229</td><td>注射用头孢曲松钠</td><td>4-抗生素</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr><tr><td>2016-01</td><td>1-立体仓库</td><td>2.08</td><td>多潘立酮胶囊</td><td>2-化学药制剂</td><td>42</td><td>16800</td><td>45298</td><td>39546</td><td>5752</td><td>1569</td></tr></tbody></table></div><h3 id="数据处理"><a href="#数据处理" class="headerlink" title="数据处理"></a>数据处理</h3><ol><li><p>计算平均存货余额【(期初存货金额+期末存货金额) / 2】、销货成本【单件销货成本 * 销售数量】。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  <span class="string">&quot;month&quot;</span>,</span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="keyword">SUM</span> ( ( <span class="string">&quot;open_inv_amount&quot;</span> + <span class="string">&quot;end_inv_amount&quot;</span> ) ) / <span class="number">2</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;avg_inv_amount&quot;</span>,<span class="comment">-- 平均存货余额</span></span><br><span class="line">  <span class="keyword">SUM</span> ( <span class="string">&quot;sale_num&quot;</span> * <span class="string">&quot;cost&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;sale_cost&quot;</span> <span class="comment">-- 销货成本</span></span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  <span class="string">&quot;inventory&quot;</span> </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;month&quot;</span> </span><br></pre></td></tr></table></figure></p></li><li><p>计算库存周转率【销货成本/平均存货余额】。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  *,</span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="string">&quot;sale_cost&quot;</span> / <span class="string">&quot;avg_inv_amount&quot;</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;ivn_turnover_ratio&quot;</span> <span class="comment">-- 库存周转率</span></span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  (...) t1 </span><br></pre></td></tr></table></figure></p></li><li><p>计算库存周转天数【360 / 库存周转率】。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">SELECT</span></span><br><span class="line">  *,</span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="number">360</span> / <span class="string">&quot;ivn_turnover_ratio&quot;</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;inv_turnover_day&quot;</span> <span class="comment">-- 库存周转天数</span></span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  (...) t2 </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;month&quot;</span></span><br></pre></td></tr></table></figure></p></li></ol><p><strong>完整 SQL:</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  *,</span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="number">360</span> / <span class="string">&quot;ivn_turnover_ratio&quot;</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;inv_turnover_day&quot;</span> <span class="comment">-- 库存周转天数</span></span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  (</span><br><span class="line">  <span class="keyword">SELECT</span></span><br><span class="line">    *,</span><br><span class="line">    <span class="keyword">ROUND</span>( <span class="string">&quot;sale_cost&quot;</span> / <span class="string">&quot;avg_inv_amount&quot;</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;ivn_turnover_ratio&quot;</span> <span class="comment">-- 库存周转率</span></span><br><span class="line">  <span class="keyword">FROM</span></span><br><span class="line">    (</span><br><span class="line">    <span class="keyword">SELECT</span></span><br><span class="line">      <span class="string">&quot;month&quot;</span>,</span><br><span class="line">      <span class="keyword">ROUND</span>( <span class="keyword">SUM</span> ( ( <span class="string">&quot;open_inv_amount&quot;</span> + <span class="string">&quot;end_inv_amount&quot;</span> ) ) / <span class="number">2</span>, <span class="number">2</span> ) <span class="keyword">AS</span> <span class="string">&quot;avg_inv_amount&quot;</span>,<span class="comment">-- 平均存货余额</span></span><br><span class="line">      <span class="keyword">SUM</span> ( <span class="string">&quot;sale_num&quot;</span> * <span class="string">&quot;cost&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;sale_cost&quot;</span> <span class="comment">-- 销货成本</span></span><br><span class="line">    <span class="keyword">FROM</span></span><br><span class="line">      <span class="string">&quot;inventory&quot;</span> </span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">      <span class="string">&quot;month&quot;</span> </span><br><span class="line">    ) t1 </span><br><span class="line">  ) t2 </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;month&quot;</span></span><br></pre></td></tr></table></figure></p><p><strong>输出数据</strong></p><div class="table-container"><table><thead><tr><th>month</th><th>avg_inv_amount</th><th>sale_cost</th><th>ivn_turnover_ratio</th><th>inv_turnover_day</th></tr></thead><tbody><tr><td>2016-01</td><td>6314146.50</td><td>471643546</td><td>74.6963</td><td>4.8195</td></tr><tr><td>2016-02</td><td>6114705.00</td><td>379228435</td><td>62.0191</td><td>5.8047</td></tr><tr><td>2016-03</td><td>6331866.00</td><td>690545294</td><td>109.0587</td><td>3.3010</td></tr><tr><td>2016-04</td><td>6404496.00</td><td>799911866</td><td>124.8985</td><td>2.8823</td></tr><tr><td>2016-05</td><td>5984480.50</td><td>299641325</td><td>50.0697</td><td>7.1900</td></tr><tr><td>2016-06</td><td>5495440.50</td><td>688046006</td><td>125.2031</td><td>2.8753</td></tr></tbody></table></div><h3 id="数据可视化"><a href="#数据可视化" class="headerlink" title="数据可视化"></a>数据可视化</h3><ol><li>绘制库存周转分析图，其中 X 轴为月份，左边 Y 轴为周转天数（柱状图），右边 Y 轴为周转率（折线图），可以看出 2016 年 11 月周转天数最小，为 0.46，周转率最高，为 777.88，库存周转情况最好，销售情况最好。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/inventory_turnover_analysis.png" alt="库存周转分析"></li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/">数据分析</category>
      
      <category domain="https://blog.eurkon.com/tags/PostgreSQL/">PostgreSQL</category>
      
      
      <comments>https://blog.eurkon.com/post/e305ad56.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>AARRR 用户运营分析</title>
      <link>https://blog.eurkon.com/post/fa6d499a.html</link>
      <guid>https://blog.eurkon.com/post/fa6d499a.html</guid>
      <pubDate>Tue, 15 Mar 2022 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;h3 id=&quot;模型概念&quot;&gt;&lt;a href=&quot;#模型概念&quot; class=&quot;headerlink&quot; title=&quot;模型概念&quot;&gt;&lt;/a&gt;模型概念&lt;/h</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><h3 id="模型概念"><a href="#模型概念" class="headerlink" title="模型概念"></a>模型概念</h3><p>AARRR 是用户获取（Acquisition）、用户激活（Activation）、用户留存（Retention）、获得收益（Revenue）、推荐传播（Referral），这个五个单词的缩写，分别对应用户生命周期中的 5 个重要环节。AARRR 模型又叫海盗模型，是用户运营过程中常用的一种模型，解释了实现用户增长的 5 个指标：获客、激活、留存、收益、传播，整个 AARRR 模型形成了用户全生命周期的闭环模式，不断扩大用户规模，实现持续增长。</p><h3 id="应用意义"><a href="#应用意义" class="headerlink" title="应用意义"></a>应用意义</h3><ol><li><p><strong>Acquisition：获取用户</strong></p><p>运营一款产品的第一步，毫无疑问是获取用户，也就是大家通常所说的推广。此时推广人员经验很就显得重要：首先要分析自己产品的特性以及目标人群，与渠道用户进行定位和匹配，要摸清楚每个渠道量级与用户质量，不同产品时期选择不同渠道，是前期铺量还是稳定期保质量。</p><ul><li>渠道量级指标：曝光量、点击、下载、安装、激活（注册激活，主动激活、推送激活、交易激活）、累计新增。</li><li>渠道质量指标：CTR（点击率），激活率，安装率，CPA 等每用户成本、用户 LTV、1次/1日用户量，用户使用时长、留存率、付费率、ARPU（每用户平均收入）。</li></ul></li><li><p><strong>Activation：提高活跃度</strong></p><p>如何将新增转化为活跃用户，是运营者面临的第一个问题。</p><p>首先我们要理解下什么是活跃？ DAU 日均活跃用户数量 = 当日新增 + 累计历史日留存</p><p>即今日活跃的用户中，一部分是新增，另外绝大部分都是以往的留存用户，产品运营周期越长，新用户占比越少。所以影响活跃最主要因素就是产品的留存表现，另外一点就是产品粘度。</p><p>分析活跃可以从两个角度出发：</p><ul><li><p>活跃用户构成</p><ul><li>新老用户占比、新老用户活跃率、忠诚用户数、回流用户数、1次/1日登录用户占比等指标，根据不同产品运营时期，不同的参考数值。</li></ul></li><li><p>产品粘度</p><ul><li>产品黏度很关键的指标，它说明了用户对产品的喜欢接纳程度，我们通常用 MAU/DAU 来定义产品的黏度指标，比值代表用户回访的天数（几天会用一次产品），当 MAU/DAU=1 的话说明这款产品用户每天都用；</li><li>DAU/MAU * 30 代表用户一个月会用几次产品。</li><li>7 日回访率、日均使用时长、日均登录次数等都是产品粘度的重要指标，分不同类型产品，依次分析。</li></ul></li></ul></li><li><p><strong>Retention：提高留存率</strong></p><p>通常维护一个老用户的成本要远远低于获取一个新用户的成本，所以熊瞎子掰苞米（掰一个，丢一个）的情况是产品运营的大忌。分析出用户在哪里流失，为什么流失，才能有的放矢的解决问题。</p><p>留存率跟产品的类型有很大关系。通常工具类应用的首月留存率可能普遍比游戏类的首月流存率要高，有些产品不是需要每日启动的，看周留存率、月留存率等指标，会更有意义。分析留存必须清楚用户是在哪些环节流失，所以每款产品，都必须有自己的流失漏斗，越细致越好，前期数据埋点要尽可能详细。另外，分析人员必须明白，你分析的是流失率还是流失占比，还是流失概率，这之间差距很大。</p><p>产品的活跃与留存息息相关，必须放在一起去分析，提升活跃与留存 4 种方式：</p><ul><li><p>有效触达，唤醒用户：指的是通过手机 PUSH、短信和微信公众号等能够触达到用户，唤醒沉睡用户启动 APP 的方式，是提升留存的非常有效的方法之一。如游戏老用户短信召回，电商老用户召回，召回肯定是有成本的，所以要根据用户以往行为，进行分析定为，找到召回率最高的那部分用户，（如 RFM 模型定为核心用户）</p></li><li><p>搭建激励体系，留存用户：好的激励体系，可以让平台健康持续发展，让用户对平台产生粘性，对提升留存非常有效。通常使用的激励方式有成长值会员体系、签到体系、积分任务体系。</p></li><li><p>丰富内容，增加用户在线时长：这点游戏产品做的非常好，各种玩法活动本身就吸引用户投入时间成本，游戏又不断强化社交属性，更增加用户粘度与成本投入。</p></li><li><p>数据反推，找到你的关键点：比如知乎，评论超过 3 次，用户就会留存下来，很难流失。比如有些游戏产品，一旦玩家跨过某个等级就就很难流失。这些都是你需要通过数据分析才能找到的关键节点。</p></li></ul><p>另外，只有留下来的才是你的用户，降低流失很重要，但也不必过分纠结于用户的流失，要清楚谁才是你的目标用户。</p></li><li><p><strong>Revenue：获取收入</strong></p><p>获取收入其实是产品运营最核心的一块。极少有人开发一款应用只是纯粹出于兴趣，绝大多数开发者最关心的就是收入。</p><ul><li>基本指标：ARPU（每用户平均收入）、ARPPU（每付费用户平均收益）、付费率（区分新老）</li><li>了解付费用户构成：高额、中额、低额用户分布</li><li>付费破冰点，付费卡点是否合理？付费点设计不合理时，付费点会变成流失点</li><li>持续付费能力分析：回购率，回购点是哪些功能，用户付费频率，时间间隔是多久？</li><li>付费功能和环节分析：不同拉收入的活动或功能，哪些反馈较好，哪部分用户反馈好，人均充值额，付费率各是多少？</li></ul><p>另外，有些产品内部会有自身货币系统，如漫画类产品的逗币流通，游戏产品的钻石，金币等产品内部货币，产出消耗是否平衡，严重关系到产品的收入。</p></li><li><p><strong>Referral：自传播</strong></p><p>病毒式传播是每个产品向往的推广方式，除了好的营销方式铺垫，更重要的还是要靠产品自身的品质。自传播中的数据指标，可以参考口碑指数、百度指数、网站 PR 值、搜索引擎收录数、反向链接数据来衡量。</p><p>自传播有个很重要的指标 K 值，K = （每个用户向他的朋友们发出的邀请的数量）*（接收到邀请的人转化为新用户的转化率），即每位用户能带来多少个新用户。当 K 值大于 1 时，说明具有自传播的能力，可以进一步通过“邀请获奖励”等运营活动进一步提高 K 值，加快传播速度。</p><p>还可以通过计算用户完成从传播到转化新用户所需要的时间（传播周期），通常传播周期越短，意味着用户裂变传播的效果越好。</p></li></ol><h2 id="实战"><a href="#实战" class="headerlink" title="实战"></a>实战</h2><h3 id="基础数据"><a href="#基础数据" class="headerlink" title="基础数据"></a>基础数据</h3><p>这里选择某 APP 的数据进行分析，数据字段为：</p><ul><li>login_time：登录时间</li><li>register_time：注册时间</li><li>is_active：是否激活</li><li>area：地区</li><li>phone：电话号码</li><li>channel：推广渠道</li><li>login_after_register_day：登录—注册天数差</li><li>member_type：会员类型</li><li>browse_time：浏览时间</li></ul><p>部分数据如下：</p><div class="table-container"><table><thead><tr><th>login_time</th><th>register_time</th><th>is_active</th><th>area</th><th>phone</th><th>channel</th><th>login_after_register_day</th><th>member_type</th><th>browse_time</th></tr></thead><tbody><tr><td>2020-04-03 00:00:00</td><td>2020-04-02 00:00:00</td><td>是</td><td>北京</td><td>189xxx2927</td><td>线下活动推广</td><td>1</td><td>低活跃客户</td><td>200</td></tr><tr><td>2020-05-05 00:00:00</td><td>2020-04-22 00:00:00</td><td>否</td><td>南京</td><td>188xxx9721</td><td>新媒体推广</td><td>13</td><td>低活跃客户</td><td>384</td></tr><tr><td>2020-09-25 00:00:00</td><td>2020-09-16 00:00:00</td><td>是</td><td>北京</td><td>138xxx6943</td><td>新媒体推广</td><td>9</td><td>普通用户</td><td>160</td></tr><tr><td>2020-05-26 00:00:00</td><td>2020-05-26 00:00:00</td><td>是</td><td>深圳</td><td>185xxx8775</td><td>线下活动推广</td><td>0</td><td>低活跃客户</td><td>559</td></tr><tr><td>2020-07-03 00:00:00</td><td>2020-06-21 00:00:00</td><td>是</td><td>成都</td><td>182xxx3136</td><td>线下活动推广</td><td>12</td><td>低活跃客户</td><td>299</td></tr><tr><td>2020-08-31 00:00:00</td><td>2020-08-12 00:00:00</td><td>是</td><td>武汉</td><td>138xxx3702</td><td>线下活动推广</td><td>19</td><td>普通用户</td><td>410</td></tr></tbody></table></div><h3 id="数据处理"><a href="#数据处理" class="headerlink" title="数据处理"></a>数据处理</h3><ol><li><p>用户获取（Acquisition）：对比各个渠道的获取用户数（获客渠道数量），平均浏览时长（获客渠道质量）。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  <span class="string">&quot;channel&quot;</span>,</span><br><span class="line">  <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;phone&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;user_num&quot;</span>,</span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="keyword">AVG</span> ( <span class="string">&quot;browse_time&quot;</span> ), <span class="number">2</span> ) <span class="keyword">AS</span> <span class="string">&quot;avg_browse_time&quot;</span> </span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  <span class="string">&quot;aarrr&quot;</span> </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;channel&quot;</span> </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;user_num&quot;</span> <span class="keyword">DESC</span></span><br></pre></td></tr></table></figure></p></li><li><p>用户激活（Activation）：每月激活率。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  <span class="string">&quot;register_month&quot;</span>,</span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;phone&quot;</span> ) FILTER ( <span class="keyword">WHERE</span> <span class="string">&quot;is_active&quot;</span> = <span class="string">&#x27;是&#x27;</span> ) :: <span class="built_in">NUMERIC</span> / <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;phone&quot;</span> ) * <span class="number">100</span>, <span class="number">2</span> ) <span class="keyword">AS</span> <span class="string">&quot;active_ratio&quot;</span> </span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  ( <span class="keyword">SELECT</span> to_char( <span class="string">&quot;register_time&quot;</span>, <span class="string">&#x27;yyyy-mm&#x27;</span> ) <span class="keyword">AS</span> <span class="string">&quot;register_month&quot;</span>, <span class="string">&quot;phone&quot;</span>, <span class="string">&quot;is_active&quot;</span> <span class="keyword">FROM</span> <span class="string">&quot;aarrr&quot;</span> ) t1 </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;register_month&quot;</span> </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;register_month&quot;</span></span><br></pre></td></tr></table></figure></p></li><li><p>用户留存（Retention）：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  <span class="string">&quot;register_month&quot;</span>,</span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;phone&quot;</span> ) FILTER ( <span class="keyword">WHERE</span> <span class="string">&quot;login_after_register_day&quot;</span> = <span class="number">1</span> ) :: <span class="built_in">NUMERIC</span> / <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;phone&quot;</span> ) * <span class="number">100</span>, <span class="number">2</span> ) <span class="keyword">AS</span> <span class="string">&quot;retent_ratio1&quot;</span>, <span class="comment">-- 次日留存率</span></span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;phone&quot;</span> ) FILTER ( <span class="keyword">WHERE</span> <span class="string">&quot;login_after_register_day&quot;</span> &lt;= <span class="number">7</span> ) :: <span class="built_in">NUMERIC</span> / <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;phone&quot;</span> ) * <span class="number">100</span>, <span class="number">2</span> ) <span class="keyword">AS</span> <span class="string">&quot;retent_ratio7&quot;</span>, <span class="comment">-- 周留存率</span></span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;phone&quot;</span> ) FILTER ( <span class="keyword">WHERE</span> <span class="string">&quot;login_after_register_day&quot;</span> &lt;= <span class="number">30</span> ) :: <span class="built_in">NUMERIC</span> / <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;phone&quot;</span> ) * <span class="number">100</span>, <span class="number">2</span> ) <span class="keyword">AS</span> <span class="string">&quot;retent_ratio30&quot;</span> <span class="comment">-- 月留存率</span></span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  ( <span class="keyword">SELECT</span> <span class="keyword">DISTINCT</span> to_char( <span class="string">&quot;register_time&quot;</span>, <span class="string">&#x27;yyyy-mm&#x27;</span> ) <span class="keyword">AS</span> <span class="string">&quot;register_month&quot;</span>, <span class="string">&quot;phone&quot;</span>, <span class="string">&quot;login_after_register_day&quot;</span> <span class="keyword">FROM</span> <span class="string">&quot;aarrr&quot;</span> ) t1 </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;register_month&quot;</span> </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;register_month&quot;</span></span><br></pre></td></tr></table></figure></p></li></ol><ol><li><p>获得收益（Revenue）：将提高用户购买活跃度作为提升收益的主要指标。将用户分为三个大类：低活跃客户、普通用户、会员。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  <span class="string">&quot;member_type&quot;</span>,</span><br><span class="line">  <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;phone&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;member_num&quot;</span> </span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  <span class="string">&quot;aarrr&quot;</span> </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;member_type&quot;</span> </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;member_num&quot;</span> <span class="keyword">DESC</span></span><br></pre></td></tr></table></figure></p></li></ol><ol><li><p>推荐传播（Referral）：使用另一个数据集计算自传播 K 值。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">SUM</span>(<span class="string">&quot;接受推荐人数&quot;</span>)/<span class="keyword">SUM</span>(<span class="string">&quot;推荐人数&quot;</span>) <span class="keyword">FROM</span> t1</span><br></pre></td></tr></table></figure></p></li></ol><h3 id="数据可视化"><a href="#数据可视化" class="headerlink" title="数据可视化"></a>数据可视化</h3><ol><li><p>用户获取：各个渠道的获取用户数（获客渠道数量），平均浏览时长（获客渠道质量）。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/aarrr_acquisition.png" alt="AARRR用户运营分析 - 用户获取"></p></li><li><p>用户激活：每月激活率。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/aarrr_activation.png" alt="AARRR用户运营分析 - 用户激活"></p></li><li><p>用户留存：次日留存率，周留存率，月留存率。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/aarrr_retention.png" alt="AARRR用户运营分析 - 用户留存"></p></li><li><p>获得收益：将用户分为三个大类：低活跃客户、普通用户、会员。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/aarrr_revenue.png" alt="AARRR用户运营分析 - 获得收益"></p></li><li><p>推荐传播：自传播 K 值。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/aarrr_referral.png" alt="AARRR用户运营分析 - 推荐传播"></p></li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/">数据分析</category>
      
      <category domain="https://blog.eurkon.com/tags/PostgreSQL/">PostgreSQL</category>
      
      
      <comments>https://blog.eurkon.com/post/fa6d499a.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>购物篮分析</title>
      <link>https://blog.eurkon.com/post/931f21f9.html</link>
      <guid>https://blog.eurkon.com/post/931f21f9.html</guid>
      <pubDate>Tue, 01 Mar 2022 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;h3 id=&quot;模型概念&quot;&gt;&lt;a href=&quot;#模型概念&quot; class=&quot;headerlink&quot; title=&quot;模型概念&quot;&gt;&lt;/a&gt;模型概念&lt;/h</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><h3 id="模型概念"><a href="#模型概念" class="headerlink" title="模型概念"></a>模型概念</h3><p>购物篮分析（Market Basket Analysis）是关联规则挖掘的应用场景，通过研究用户在一次购买行为中放入购物篮中不同商品之间的关联，研究顾客的购买行为，从而辅助零售企业制定营销策略的一种关联分析方法。</p><p>购物篮分析使用分析商品关联性可以用以下指标进行衡量：</p><div class="table-container"><table><thead><tr><th>指标</th><th>定义</th><th>概率描述</th><th>举例说明</th></tr></thead><tbody><tr><td>支持度</td><td>支持度是指 A 商品和 B 商品同时被购买的概率，或者说某个商品组合的购买次数占总商品购买次数的比例。支持度说明了这条规则在所有事务中有多大的代表性，显然支持度越大，关联规则越重要。</td><td>物品集 A 对物品集 B 的支持度  <code>P(A ∩ B)</code></td><td>今天共有 10 笔订单，其中同时购买牛奶和面包的次数是 6 次，那么 <code>牛奶+面包</code> 组合的支持度就是 6/10=60%</td></tr><tr><td>置信度</td><td>置信度是指购买 A 之后又购买 B 的条件概率，简单说就是因为购买了 A 所以购买了 B 的概率</td><td>物品集 B 对物品集 A 的置信度 confidence <code>P(B&vert;A) = P(A ∩ B) / P(A)</code></td><td>今天共有 10 笔订单，其中购买 A 的次数是 8，同时购买 A 和 B 的次数是 6，则其置信度是 6/8=75%</td></tr><tr><td>提升度</td><td>先购买 A 对购买 B 的提升作用，用来判断商品组合方式是否具有实际价值，是看组合商品被购买的次数是否高于单独商品的购买次数，大于 1 说明该组合方式有效，小于 1 则说明无效。</td><td><code>L = P(A ∩ B) / [ P(A)*P(B)]</code></td><td>今天共有 10 笔订单，购买 A 的次数是 8，购买 B 的次数是 6，购买 A+B 的次数是 6，那么提升度是 0.6/(0.8*0.6)&gt;1，因此 A+B 的组合方式是有效的。</td></tr></tbody></table></div><h3 id="应用意义"><a href="#应用意义" class="headerlink" title="应用意义"></a>应用意义</h3><p>购物篮分析目的是从大规模订单数据集中寻找商品之间的关联，线下零售商可藉由此分析改变货架上的商品排列或是设计吸引客户的组合促销套餐等，线上电商则可以做商品推荐，最著名的案例是啤酒和尿片的故事。</p><h2 id="实战"><a href="#实战" class="headerlink" title="实战"></a>实战</h2><h3 id="基础数据"><a href="#基础数据" class="headerlink" title="基础数据"></a>基础数据</h3><p>这里选择面包店销售数据集进行分析，数据字段为：</p><ul><li>TransactionNo：每笔交易的唯一标识符</li><li>Items：购买的物品</li><li>DateTime：交易的日期和时间戳</li><li>Daypart：进行交易的一天中的一部分（早上、下午、傍晚、晚上）</li><li>DayType：分类交易是在周末还是工作日进行</li></ul><p>部分数据如下：</p><div class="table-container"><table><thead><tr><th>TransactionNo</th><th>Items</th><th>DateTime</th><th>Daypart</th><th>DayType</th></tr></thead><tbody><tr><td>1</td><td>Bread</td><td>2016-10-30 09:58:11</td><td>Morning</td><td>Weekend</td></tr><tr><td>2</td><td>Scandinavian</td><td>2016-10-30 10:05:34</td><td>Morning</td><td>Weekend</td></tr><tr><td>2</td><td>Scandinavian</td><td>2016-10-30 10:05:34</td><td>Morning</td><td>Weekend</td></tr><tr><td>3</td><td>Hot chocolate</td><td>2016-10-30 10:07:57</td><td>Morning</td><td>Weekend</td></tr><tr><td>3</td><td>Jam</td><td>2016-10-30 10:07:57</td><td>Morning</td><td>Weekend</td></tr><tr><td>3</td><td>Cookies</td><td>2016-10-30 10:07:57</td><td>Morning</td><td>Weekend</td></tr></tbody></table></div><p>数据下载地址：<a href="https://www.kaggle.com/datasets/akashdeepkuila/bakery">Bakery Sales Dataset</a></p><h3 id="数据处理"><a href="#数据处理" class="headerlink" title="数据处理"></a>数据处理</h3><ol><li><p>因为物品种类较多，这里只筛选 <code>day_part = &#39;Evening&#39;</code>，<code>day_type = &#39;Weekend&#39;</code>，过滤只购买单一物品的销售数据。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">WITH</span> t1 <span class="keyword">AS</span> (</span><br><span class="line">  <span class="keyword">SELECT</span> <span class="keyword">DISTINCT</span></span><br><span class="line">    <span class="string">&quot;transaction_no&quot;</span>,</span><br><span class="line">    <span class="string">&quot;items&quot;</span> </span><br><span class="line">  <span class="keyword">FROM</span></span><br><span class="line">    <span class="string">&quot;bakery&quot;</span> tx </span><br><span class="line">  <span class="keyword">WHERE</span></span><br><span class="line">    <span class="string">&quot;day_part&quot;</span> = <span class="string">&#x27;Evening&#x27;</span> </span><br><span class="line">    <span class="keyword">AND</span> <span class="string">&quot;day_type&quot;</span> = <span class="string">&#x27;Weekend&#x27;</span> </span><br><span class="line">    <span class="keyword">AND</span> <span class="keyword">EXISTS</span> (</span><br><span class="line">    <span class="keyword">SELECT</span></span><br><span class="line">      <span class="string">&quot;transaction_no&quot;</span> </span><br><span class="line">    <span class="keyword">FROM</span></span><br><span class="line">      <span class="string">&quot;bakery&quot;</span> ty </span><br><span class="line">    <span class="keyword">WHERE</span></span><br><span class="line">      <span class="string">&quot;day_part&quot;</span> = <span class="string">&#x27;Evening&#x27;</span> </span><br><span class="line">      <span class="keyword">AND</span> <span class="string">&quot;day_type&quot;</span> = <span class="string">&#x27;Weekend&#x27;</span> </span><br><span class="line">      <span class="keyword">AND</span> tx.<span class="string">&quot;transaction_no&quot;</span> = ty.<span class="string">&quot;transaction_no&quot;</span> </span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">      <span class="string">&quot;transaction_no&quot;</span> </span><br><span class="line">    <span class="keyword">HAVING</span></span><br><span class="line">      <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;items&quot;</span> ) &gt; <span class="number">1</span> <span class="comment">-- 购买两个以上不同物品</span></span><br><span class="line">    ) </span><br><span class="line">  )</span><br></pre></td></tr></table></figure></p></li><li><p>关联同一订单的其他商品，关联购买物品 A 的订单数量，关联购买物品 B 的订单数量，关联订单数量；过滤物品 A 和 物品 B 是同一物品，否则分析没有意义。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">DISTINCT</span></span><br><span class="line">  t1.<span class="string">&quot;items&quot;</span> <span class="keyword">AS</span> <span class="string">&quot;items_a&quot;</span>,</span><br><span class="line">  t2.<span class="string">&quot;items&quot;</span> <span class="keyword">AS</span> <span class="string">&quot;items_b&quot;</span>,</span><br><span class="line">  t3.<span class="string">&quot;a_num&quot;</span>,</span><br><span class="line">  t4.<span class="string">&quot;b_num&quot;</span>,</span><br><span class="line">  <span class="keyword">SUM</span> ( <span class="number">1</span> ) <span class="keyword">OVER</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> t1.<span class="string">&quot;items&quot;</span>, t2.<span class="string">&quot;items&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;a_b_num&quot;</span>, <span class="comment">-- 同时购买物品 A 和物品 B 的订单数</span></span><br><span class="line">  t5.<span class="string">&quot;total_num&quot;</span> </span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  t1</span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> t1 <span class="keyword">AS</span> t2 <span class="keyword">ON</span> t1.<span class="string">&quot;transaction_no&quot;</span> = t2.<span class="string">&quot;transaction_no&quot;</span> <span class="comment">-- 关联同一订单的其他商品</span></span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> ( <span class="keyword">SELECT</span> <span class="string">&quot;items&quot;</span>, <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;transaction_no&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;a_num&quot;</span> <span class="keyword">FROM</span> t1 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="string">&quot;items&quot;</span> ) t3 <span class="keyword">ON</span> t1.<span class="string">&quot;items&quot;</span> = t3.<span class="string">&quot;items&quot;</span> <span class="comment">-- 关联购买物品 A 的订单数量</span></span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> ( <span class="keyword">SELECT</span> <span class="string">&quot;items&quot;</span>, <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;transaction_no&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;b_num&quot;</span> <span class="keyword">FROM</span> t1 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="string">&quot;items&quot;</span> ) t4 <span class="keyword">ON</span> t2.<span class="string">&quot;items&quot;</span> = t4.<span class="string">&quot;items&quot;</span> <span class="comment">-- 关联购买物品 B 的订单数量</span></span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> ( <span class="keyword">SELECT</span> <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;transaction_no&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;total_num&quot;</span> <span class="keyword">FROM</span> t1 ) t5 <span class="keyword">ON</span> <span class="number">1</span> = <span class="number">1</span>  <span class="comment">-- 关联订单数量</span></span><br><span class="line"><span class="keyword">WHERE</span></span><br><span class="line">  t1.<span class="string">&quot;items&quot;</span> != t2.<span class="string">&quot;items&quot;</span> <span class="comment">-- 过滤物品 A 和 物品 B 是同一物品</span></span><br></pre></td></tr></table></figure></p></li><li><p>计算支持度、置信度和提升度。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  *,</span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="string">&quot;a_b_num&quot;</span> :: <span class="built_in">NUMERIC</span> / <span class="string">&quot;total_num&quot;</span> * <span class="number">100</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;support&quot;</span>, <span class="comment">-- 支持度</span></span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="string">&quot;a_b_num&quot;</span> :: <span class="built_in">NUMERIC</span> / <span class="string">&quot;a_num&quot;</span> * <span class="number">100</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;confidence&quot;</span>, <span class="comment">-- 置信度</span></span><br><span class="line">  <span class="keyword">ROUND</span>( ( <span class="string">&quot;a_b_num&quot;</span> :: <span class="built_in">NUMERIC</span> / <span class="string">&quot;total_num&quot;</span> ) / ( ( <span class="string">&quot;a_num&quot;</span> :: <span class="built_in">NUMERIC</span> / <span class="string">&quot;total_num&quot;</span> ) * ( <span class="string">&quot;b_num&quot;</span> :: <span class="built_in">NUMERIC</span> / <span class="string">&quot;total_num&quot;</span> ) ) * <span class="number">100</span>, <span class="number">4</span> ) <span class="string">&quot;promotion&quot;</span> <span class="comment">-- 提升度</span></span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  (...) t6 </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;items_a&quot;</span>,</span><br><span class="line">  <span class="string">&quot;items_b&quot;</span></span><br></pre></td></tr></table></figure></p></li></ol><p><strong>完整 SQL:</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">WITH</span> t1 <span class="keyword">AS</span> (</span><br><span class="line">  <span class="keyword">SELECT</span> <span class="keyword">DISTINCT</span></span><br><span class="line">    <span class="string">&quot;transaction_no&quot;</span>,</span><br><span class="line">    <span class="string">&quot;items&quot;</span> </span><br><span class="line">  <span class="keyword">FROM</span></span><br><span class="line">    <span class="string">&quot;bakery&quot;</span> tx </span><br><span class="line">  <span class="keyword">WHERE</span></span><br><span class="line">    <span class="string">&quot;day_part&quot;</span> = <span class="string">&#x27;Evening&#x27;</span> </span><br><span class="line">    <span class="keyword">AND</span> <span class="string">&quot;day_type&quot;</span> = <span class="string">&#x27;Weekend&#x27;</span> </span><br><span class="line">    <span class="keyword">AND</span> <span class="keyword">EXISTS</span> (</span><br><span class="line">    <span class="keyword">SELECT</span></span><br><span class="line">      <span class="string">&quot;transaction_no&quot;</span> </span><br><span class="line">    <span class="keyword">FROM</span></span><br><span class="line">      <span class="string">&quot;bakery&quot;</span> ty </span><br><span class="line">    <span class="keyword">WHERE</span></span><br><span class="line">      <span class="string">&quot;day_part&quot;</span> = <span class="string">&#x27;Evening&#x27;</span> </span><br><span class="line">      <span class="keyword">AND</span> <span class="string">&quot;day_type&quot;</span> = <span class="string">&#x27;Weekend&#x27;</span> </span><br><span class="line">      <span class="keyword">AND</span> tx.<span class="string">&quot;transaction_no&quot;</span> = ty.<span class="string">&quot;transaction_no&quot;</span> </span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">      <span class="string">&quot;transaction_no&quot;</span> </span><br><span class="line">    <span class="keyword">HAVING</span></span><br><span class="line">      <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;items&quot;</span> ) &gt; <span class="number">1</span> <span class="comment">-- 购买两个以上不同物品</span></span><br><span class="line">    ) </span><br><span class="line">  )</span><br><span class="line"></span><br><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  *,</span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="string">&quot;a_b_num&quot;</span> :: <span class="built_in">NUMERIC</span> / <span class="string">&quot;total_num&quot;</span> * <span class="number">100</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;support&quot;</span>, <span class="comment">-- 支持度</span></span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="string">&quot;a_b_num&quot;</span> :: <span class="built_in">NUMERIC</span> / <span class="string">&quot;a_num&quot;</span> * <span class="number">100</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;confidence&quot;</span>, <span class="comment">-- 置信度</span></span><br><span class="line">  <span class="keyword">ROUND</span>( ( <span class="string">&quot;a_b_num&quot;</span> :: <span class="built_in">NUMERIC</span> / <span class="string">&quot;total_num&quot;</span> ) / ( ( <span class="string">&quot;a_num&quot;</span> :: <span class="built_in">NUMERIC</span> / <span class="string">&quot;total_num&quot;</span> ) * ( <span class="string">&quot;b_num&quot;</span> :: <span class="built_in">NUMERIC</span> / <span class="string">&quot;total_num&quot;</span> ) ) * <span class="number">100</span>, <span class="number">4</span> ) <span class="string">&quot;promotion&quot;</span> <span class="comment">-- 提升度</span></span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  (<span class="keyword">SELECT</span> <span class="keyword">DISTINCT</span></span><br><span class="line">    t1.<span class="string">&quot;items&quot;</span> <span class="keyword">AS</span> <span class="string">&quot;items_a&quot;</span>,</span><br><span class="line">    t2.<span class="string">&quot;items&quot;</span> <span class="keyword">AS</span> <span class="string">&quot;items_b&quot;</span>,</span><br><span class="line">    t3.<span class="string">&quot;a_num&quot;</span>,</span><br><span class="line">    t4.<span class="string">&quot;b_num&quot;</span>,</span><br><span class="line">    <span class="keyword">SUM</span> ( <span class="number">1</span> ) <span class="keyword">OVER</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> t1.<span class="string">&quot;items&quot;</span>, t2.<span class="string">&quot;items&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;a_b_num&quot;</span>, <span class="comment">-- 同时购买物品 A 和物品 B 的订单数</span></span><br><span class="line">    t5.<span class="string">&quot;total_num&quot;</span> </span><br><span class="line">  <span class="keyword">FROM</span></span><br><span class="line">    t1</span><br><span class="line">    <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> t1 <span class="keyword">AS</span> t2 <span class="keyword">ON</span> t1.<span class="string">&quot;transaction_no&quot;</span> = t2.<span class="string">&quot;transaction_no&quot;</span> <span class="comment">-- 关联同一订单的其他商品</span></span><br><span class="line">    <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> ( <span class="keyword">SELECT</span> <span class="string">&quot;items&quot;</span>, <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;transaction_no&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;a_num&quot;</span> <span class="keyword">FROM</span> t1 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="string">&quot;items&quot;</span> ) t3 <span class="keyword">ON</span> t1.<span class="string">&quot;items&quot;</span> = t3.<span class="string">&quot;items&quot;</span> <span class="comment">-- 关联购买物品 A 的订单数量</span></span><br><span class="line">    <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> ( <span class="keyword">SELECT</span> <span class="string">&quot;items&quot;</span>, <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;transaction_no&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;b_num&quot;</span> <span class="keyword">FROM</span> t1 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="string">&quot;items&quot;</span> ) t4 <span class="keyword">ON</span> t2.<span class="string">&quot;items&quot;</span> = t4.<span class="string">&quot;items&quot;</span> <span class="comment">-- 关联购买物品 B 的订单数量</span></span><br><span class="line">    <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> ( <span class="keyword">SELECT</span> <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;transaction_no&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;total_num&quot;</span> <span class="keyword">FROM</span> t1 ) t5 <span class="keyword">ON</span> <span class="number">1</span> = <span class="number">1</span>  <span class="comment">-- 关联订单数量</span></span><br><span class="line">  <span class="keyword">WHERE</span></span><br><span class="line">    t1.<span class="string">&quot;items&quot;</span> != t2.<span class="string">&quot;items&quot;</span> <span class="comment">-- 过滤物品 A 和 物品 B 是同一物品</span></span><br><span class="line">) t6 </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;items_a&quot;</span>,</span><br><span class="line">  <span class="string">&quot;items_b&quot;</span></span><br></pre></td></tr></table></figure></p><p><strong>输出数据</strong></p><div class="table-container"><table><thead><tr><th>items_a</th><th>items_b</th><th>a_num</th><th>b_num</th><th>a_b_num</th><th>total_num</th><th>support</th><th>confidence</th><th>promotion</th></tr></thead><tbody><tr><td>Alfajores</td><td>Bread</td><td>1</td><td>7</td><td>1</td><td>37</td><td>2.7027</td><td>100.0000</td><td>528.5714</td></tr><tr><td>Alfajores</td><td>Cake</td><td>1</td><td>7</td><td>1</td><td>37</td><td>2.7027</td><td>100.0000</td><td>528.5714</td></tr><tr><td>Alfajores</td><td>Coffee</td><td>1</td><td>12</td><td>1</td><td>37</td><td>2.7027</td><td>100.0000</td><td>308.3333</td></tr><tr><td>Art Tray</td><td>Tartine</td><td>1</td><td>1</td><td>1</td><td>37</td><td>2.7027</td><td>100.0000</td><td>3700.0000</td></tr><tr><td>Bread</td><td>Alfajores</td><td>7</td><td>1</td><td>1</td><td>37</td><td>2.7027</td><td>14.2857</td><td>528.5714</td></tr><tr><td>Bread</td><td>Cake</td><td>7</td><td>7</td><td>2</td><td>37</td><td>5.4054</td><td>28.5714</td><td>151.0204</td></tr></tbody></table></div><h3 id="数据可视化"><a href="#数据可视化" class="headerlink" title="数据可视化"></a>数据可视化</h3><ol><li><p>绘制气泡图，其中 X 轴为支持度，Y 轴为置信度，气泡大小表示提升度。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/basket_bubble.png" alt="购物篮分析 - 气泡图"></p></li><li><p>绘制物品 A 对物品 B 支持度的矩阵图，表示同时购买 A 和 B 的概率。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/basket_support.png" alt="购物篮分析 - 支持度矩阵图"></p></li><li><p>绘制物品 A 对物品 B 置信度的矩阵图，表示因为购买了 A 所以购买了 B 的概率。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/basket_confidence.png" alt="购物篮分析 - 置信度矩阵图"></p></li><li><p>绘制物品 A 对物品 B 提升度的矩阵图，表示组合商品被购买的次数是否高于单独商品的购买次数，大于 100% 表示有效。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/basket_promotion.png" alt="购物篮分析 - 提升度矩阵图"></p></li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/">数据分析</category>
      
      <category domain="https://blog.eurkon.com/tags/PostgreSQL/">PostgreSQL</category>
      
      
      <comments>https://blog.eurkon.com/post/931f21f9.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>电商转化漏斗模型</title>
      <link>https://blog.eurkon.com/post/72736a6d.html</link>
      <guid>https://blog.eurkon.com/post/72736a6d.html</guid>
      <pubDate>Tue, 15 Feb 2022 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;h3 id=&quot;模型概念&quot;&gt;&lt;a href=&quot;#模型概念&quot; class=&quot;headerlink&quot; title=&quot;模型概念&quot;&gt;&lt;/a&gt;模型概念&lt;/h</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><h3 id="模型概念"><a href="#模型概念" class="headerlink" title="模型概念"></a>模型概念</h3><p>电商转化作为电商运营重点关注的一个环节，是千万卖家最关注的，也是最难界定的指标。转化漏斗模型，是分析用户在使用某种业务的情景下，经过一系列步骤转化效果的方法，转化分析的本质是为了促进企业的核心业务的流通，最大化每个营销漏斗的转化率。</p><h3 id="应用意义"><a href="#应用意义" class="headerlink" title="应用意义"></a>应用意义</h3><p>在理想的情况，用户会沿着产品设计的路径到达最终目标事件，但实际情况是用户的行为路径是多种多样的。通过埋点事件配置关键业务路径，可以分析多种业务场景下转化和流失的情况，不仅能够找出产品潜在问题的位置，还可以定位每个环节的流失用户，进而定向营销促转化。</p><h2 id="实战"><a href="#实战" class="headerlink" title="实战"></a>实战</h2><h3 id="基础数据"><a href="#基础数据" class="headerlink" title="基础数据"></a>基础数据</h3><p>这里选择 电商客户行为日志数据进行分析，数据字段为：</p><ul><li>event_time：事件时间</li><li>event_type：事件类型（view、cart、purchase）</li><li>product_id：产品 ID</li><li>category_id：产品分类 ID</li><li>category_code：产品分类</li><li>brand：品牌</li><li>price：价格</li><li>user_id：用户 ID</li><li>user_session：用户 Session</li></ul><p>部分数据如下：</p><div class="table-container"><table><thead><tr><th>event_time</th><th>event_type</th><th>product_id</th><th>category_id</th><th>category_code</th><th>brand</th><th>price</th><th>user_id</th><th>user_session</th></tr></thead><tbody><tr><td>2020-09-24 11:57:06 UTC</td><td>view</td><td>1996170</td><td>2144415922528452715</td><td>electronics.telephone</td><td></td><td>31.90</td><td>1515915625519388267</td><td>LJuJVLEjPT</td></tr><tr><td>2020-09-24 11:57:26 UTC</td><td>view</td><td>139905</td><td>2144415926932472027</td><td>computers.components.cooler</td><td>zalman</td><td>17.16</td><td>1515915625519380411</td><td>tdicluNnRY</td></tr><tr><td>2020-09-24 11:57:27 UTC</td><td>view</td><td>215454</td><td>2144415927158964449</td><td></td><td></td><td>9.81</td><td>1515915625513238515</td><td>4TMArHtXQy</td></tr><tr><td>2020-09-24 11:57:33 UTC</td><td>view</td><td>635807</td><td>2144415923107266682</td><td>computers.peripherals.printer</td><td>pantum</td><td>113.81</td><td>1515915625519014356</td><td>aGFYrNgC08</td></tr><tr><td>2020-09-24 11:57:36 UTC</td><td>view</td><td>3658723</td><td>2144415921169498184</td><td></td><td>cameronsino</td><td>15.87</td><td>1515915625510743344</td><td>aa4mmk0kwQ</td></tr><tr><td>2020-09-24 11:57:59 UTC</td><td>view</td><td>664325</td><td>2144415951611757447</td><td>construction.tools.saw</td><td>carver</td><td>52.33</td><td>1515915625519388062</td><td>vnkdP81DDW</td></tr></tbody></table></div><p>数据下载地址：<a href="https://www.kaggle.com/datasets/mkechinov/ecommerce-events-history-in-electronics-store">eCommerce events history in electronics store</a></p><h3 id="数据处理"><a href="#数据处理" class="headerlink" title="数据处理"></a>数据处理</h3><ol><li><p>按照不同的事件类型 <code>view：浏览</code>、<code>cart：加购</code>、<code>purchase：下单</code>（其他数据可能还有<code>搜索</code>、<code>展示</code>、<code>咨询</code>、<code>评价</code>等行为）分组聚合，去重计算个分组的用户数。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">WITH</span> t1 <span class="keyword">AS</span> (</span><br><span class="line">  <span class="keyword">SELECT</span></span><br><span class="line">    <span class="keyword">CASE</span> <span class="string">&quot;event_type&quot;</span> </span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&#x27;view&#x27;</span> <span class="keyword">THEN</span> <span class="number">0</span> </span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&#x27;cart&#x27;</span> <span class="keyword">THEN</span> <span class="number">1</span> </span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&#x27;purchase&#x27;</span> <span class="keyword">THEN</span> <span class="number">2</span> </span><br><span class="line">    <span class="keyword">END</span> <span class="keyword">AS</span> <span class="string">&quot;events_rank&quot;</span>,</span><br><span class="line">    <span class="keyword">CASE</span> <span class="string">&quot;event_type&quot;</span> </span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&#x27;view&#x27;</span> <span class="keyword">THEN</span> <span class="string">&#x27;浏览&#x27;</span> </span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&#x27;cart&#x27;</span> <span class="keyword">THEN</span> <span class="string">&#x27;加购&#x27;</span> </span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&#x27;purchase&#x27;</span> <span class="keyword">THEN</span> <span class="string">&#x27;下单&#x27;</span> </span><br><span class="line">    <span class="keyword">END</span> <span class="keyword">AS</span> <span class="string">&quot;event_type&quot;</span>,</span><br><span class="line">    <span class="keyword">COUNT</span> ( <span class="keyword">DISTINCT</span> <span class="string">&quot;user_id&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;user_num&quot;</span> </span><br><span class="line">  <span class="keyword">FROM</span></span><br><span class="line">    <span class="string">&quot;events&quot;</span> </span><br><span class="line">  <span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">    <span class="string">&quot;event_type&quot;</span> </span><br><span class="line">  )</span><br></pre></td></tr></table></figure></p></li><li><p>按照事件行为路径顺序，匹配上一阶段的用户数，计算各阶段的转化率。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  t1.* ,</span><br><span class="line">  <span class="keyword">ROUND</span>( t1.<span class="string">&quot;user_num&quot;</span> :: <span class="built_in">NUMERIC</span> / t2.<span class="string">&quot;user_num&quot;</span> * <span class="number">100</span>, <span class="number">4</span> ) convert_ratio </span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  t1</span><br><span class="line">  <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> t1 <span class="keyword">AS</span> t2 <span class="keyword">ON</span> t1.<span class="string">&quot;events_rank&quot;</span> = t2.<span class="string">&quot;events_rank&quot;</span> + <span class="number">1</span> </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  t1.<span class="string">&quot;events_rank&quot;</span></span><br></pre></td></tr></table></figure></p></li></ol><p><strong>输出数据</strong></p><div class="table-container"><table><thead><tr><th>events_rank</th><th>event_type</th><th>user_num</th><th>convert_ratio</th></tr></thead><tbody><tr><td>0</td><td>浏览</td><td>16346</td><td></td></tr><tr><td>1</td><td>加购</td><td>1135</td><td>6.9436</td></tr><tr><td>2</td><td>下单</td><td>675</td><td>59.4714</td></tr></tbody></table></div><h3 id="数据可视化"><a href="#数据可视化" class="headerlink" title="数据可视化"></a>数据可视化</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/e-commerce_conversion.png" alt="电商转化漏斗模型"></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/">数据分析</category>
      
      <category domain="https://blog.eurkon.com/tags/PostgreSQL/">PostgreSQL</category>
      
      
      <comments>https://blog.eurkon.com/post/72736a6d.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>波士顿矩阵</title>
      <link>https://blog.eurkon.com/post/dd96e05f.html</link>
      <guid>https://blog.eurkon.com/post/dd96e05f.html</guid>
      <pubDate>Tue, 01 Feb 2022 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;h3 id=&quot;模型概念&quot;&gt;&lt;a href=&quot;#模型概念&quot; class=&quot;headerlink&quot; title=&quot;模型概念&quot;&gt;&lt;/a&gt;模型概念&lt;/h</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><h3 id="模型概念"><a href="#模型概念" class="headerlink" title="模型概念"></a>模型概念</h3><p>波士顿矩阵（BCG Matrix），又称市场增长率--相对市场份额矩阵、波士顿咨询集团法、四象限分析法、产品系列结构管理法等，由美国著名的管理学家、波士顿咨询公司创始人布鲁斯·亨德森于 1970 年创作。</p><p>波士顿矩阵认为一般决定产品结构的基本因素有两个：即<strong>市场引力</strong>与<strong>企业实力</strong>，通过销售增长率（反映市场引力的指标）和市场份额（反映企业实力的指标）来分析决定企业的产品结构。</p><ul><li>市场引力：包括整个市场的销售量（额）增长率、竞争对手强弱及利润高低等。其中最主要的是反映市场引力的综合指标--销售增长率，这是决定企业产品结构是否合理的外在因素。</li><li>企业实力：包括市场份额，技术、设备、资金利用能力等，其中市场份额是决定企业产品结构的内在要素，它直接显示出企业竞争实力。销售增长率与市场份额既相互影响，又互为条件：市场引力大，市场占有高，可以显示产品发展的良好前景，企业也具备相应的适应能力，实力较强；如果仅有市场引力大，而没有相应的高市场份额，则说明企业尚无足够实力，则该种产品也无法顺利发展。相反，企业实力强，而市场引力小的产品也预示了该产品的市场前景不佳。</li></ul><p>通过以上两个因素相互作用，会出现四种不同性质的产品类型，形成不同的产品发展前景：</p><ul><li>明星类产品：销售增长率和市场份额“双高”的产品群；</li><li>瘦狗类产品：销售增长率和市场份额“双低”的产品群；</li><li>问题类产品：销售增长率高、市场份额低的产品群；</li><li>金牛类产品：销售增长率低、市场份额高的产品群。</li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/cover/bcg_matrix.png" alt></p><h3 id="应用意义"><a href="#应用意义" class="headerlink" title="应用意义"></a>应用意义</h3><p>波士顿矩阵对于企业产品所处的四个象限具有不同的定义和相应的战略对策。</p><ul><li><p>明星类业务（Stars，指高增长、高市场份额）</p><p>这个领域中的产品处于快速增长的市场中并且占有支配地位份额。但也许不会产生正现金流量。但因为市场还在高速成业必须继续投资，以保持与市场同步增长，并击退竞争对手。</p></li><li><p>问题类业务（Question Marks，指高增长、低市场份额）</p><p>处在这个领域中的是一些投机性产品。这些产品可能利润率但占有的市场份额很小。公司必须慎重回答“是否继续投资．业务?”这个问题。只有那些符合企业发展长远目标、企业具优势、能够增强企业核心竞争力的业务才得到肯定的回答。</p></li><li><p>瘦狗型业务（Dogs，指低增长、低市场份额）</p><p>这个剩下的领域中的产品既不能产生大量的现金，也不需要投入大量现金，这些产品没有希望改进其绩效。瘦狗型业务通常要占用很多资源。多数时候是得不偿失的。</p></li><li><p>金牛类业务（Cash Cows，指低增长、高市场份额）</p><p>处在这个领域中的产品产生大量的现金。但未来的增长前景是有限的。由于市场已经成熟。企业不必大量投资来扩展市场规模．同时作为市场中的领导者。该业务享有规模经济和高边际利润的优势，因而给企业带来大量现金流。</p></li></ul><p>充分了解了四种业务的特点后还须进一步明确各项业务单位在公司中的不同地位，从而进一步明确其战略目标。通常有四种战略目标分别适用于不同的业务。</p><ul><li>发展：以提高经营单位的相对市场份额为目标，甚至不惜放弃短期收益。要使问题类业务想尽快成为“明星”，就要增加资金投入。</li><li>稳定；投资维持现状，目标是保持业务单位现有的市场份额、对于较大的“金牛”可以此为目标，以使它们产生更多的收益。</li><li>收缩：这种战略主要是为了获得短期收益，目标是在短期内尽可能地得到最大限度的现金收入。对处境不佳的金牛类业务及没有发展前途的问题类业务和瘦狗类业务应视具体情况采取这种策略。</li><li>放弃：目标在于清理和撤销某些业务，减轻负担，以便将有限的资源用于效益较高的业务。这种目标适用于无利可图的瘦狗类和问题类业务。一个公司必须对其业务加以调整，以使其投资组合趋于合理。</li></ul><h2 id="实战"><a href="#实战" class="headerlink" title="实战"></a>实战</h2><h3 id="基础数据"><a href="#基础数据" class="headerlink" title="基础数据"></a>基础数据</h3><p>这里选择 2019 年某一周苹果商店应用榜单排名的数据进行分析，数据字段为：</p><ul><li>appid：应用 ID</li><li>date：日期（2019-11-30 -- 2019-12-06）</li><li>category：应用类别</li><li>feed：榜单类型（free--免费，paid--付费，grossing--畅销）</li><li>name：应用名称</li><li>publisher：应用发行商</li><li>price：应用价格</li><li>ranking：应用排名</li><li>change：应用排名变化（今日排名-昨日排名）</li><li>sub_ranking：应用类型榜单排名</li><li>sub_change：应用类型榜单排名变化（今日排名-昨日排名）</li><li>comment_rating：应用评分</li><li>comment_num：评论数量</li><li>keyword_cover：关键词封面</li></ul><p>部分数据如下：</p><div class="table-container"><table><thead><tr><th>appid</th><th>date</th><th>category</th><th>feed</th><th>name</th><th>publisher</th><th>price</th><th>ranking</th><th>change</th><th>sub_ranking</th><th>sub_change</th><th>comment_rating</th><th>comment_num</th><th>keyword_cover</th></tr></thead><tbody><tr><td>691828408</td><td>2019-11-30</td><td>5000</td><td>free</td><td>微视-短视频创作与分享</td><td>Tencent Technology (Beijing) Company Limited</td><td>0.00</td><td>1</td><td>12</td><td>1</td><td>5</td><td>4.5</td><td>124000</td><td>17503</td></tr><tr><td>1458072671</td><td>2019-11-30</td><td>5000</td><td>free</td><td>剪映 - 轻而易剪</td><td>深圳市脸萌科技有限公司</td><td>0.00</td><td>2</td><td>-1</td><td>2</td><td>-1</td><td>4.9</td><td>632000</td><td>19978</td></tr><tr><td>1448327606</td><td>2019-11-30</td><td>5000</td><td>free</td><td>刷宝短视频</td><td>成都力奥文化传播有限公司</td><td>0.00</td><td>3</td><td>-1</td><td>3</td><td>-1</td><td>4.8</td><td>599000</td><td>6759</td></tr><tr><td>1472502819</td><td>2019-11-30</td><td>5000</td><td>free</td><td>快手极速版</td><td>华艺汇龙</td><td>0.00</td><td>4</td><td>-1</td><td>4</td><td>-1</td><td>4.9</td><td>228000</td><td>38549</td></tr><tr><td>1142110895</td><td>2019-11-30</td><td>5000</td><td>free</td><td>抖音短视频</td><td>Beijing Microlive Vision Technology Co., Ltd</td><td>0.00</td><td>5</td><td>-1</td><td>5</td><td>-1</td><td>4.9</td><td>23870000</td><td>21441</td></tr><tr><td>1468454200</td><td>2019-11-30</td><td>5000</td><td>free</td><td>番茄小说-原红果小说</td><td>Beijing Zhending Technology Co., Ltd.</td><td>0.00</td><td>6</td><td>-1</td><td>1</td><td>0</td><td>4.8</td><td>183000</td><td>16171</td></tr></tbody></table></div><p>数据下载地址：<a href="https://www.kaggle.com/datasets/iamsk7/apple-store-ranks-2019">apple store ranks 2019</a></p><h3 id="数据处理"><a href="#数据处理" class="headerlink" title="数据处理"></a>数据处理</h3><p>由于数据较多，下面只选取分类为 <code>5000</code>、榜单类型为 <code>free</code>、排名 <code>ranking</code> 在 <code>100</code> 以内的应用排名数据，以七天的平均评论数占比 <code>AVG(comment_num/SUM(comment_num))</code> 作为市场份额计算市场份额占比，以七天平均评论数增长率 <code>(end_ranking/start_ranking)^-6 - 1</code> 计算销售增长率（这里没有类似每日安装数量的数据字段，如果有可以使用）：</p><ol><li><p>获取每日免费榜单前 100 的应用，查到部分日期有重复数据，这里按应用名称和日期聚合，取最高排名和最大评论数。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">WITH</span> t1 <span class="keyword">AS</span> (</span><br><span class="line">  <span class="keyword">SELECT</span></span><br><span class="line">    <span class="string">&quot;appid&quot;</span>,</span><br><span class="line">    <span class="string">&quot;name&quot;</span>,</span><br><span class="line">    <span class="string">&quot;date&quot;</span>,</span><br><span class="line">    <span class="keyword">MIN</span> ( <span class="string">&quot;ranking&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;ranking&quot;</span>,</span><br><span class="line">    <span class="keyword">MAX</span> ( <span class="string">&quot;comment_num&quot;</span> ) <span class="string">&quot;comment_num&quot;</span> </span><br><span class="line">  <span class="keyword">FROM</span></span><br><span class="line">    <span class="string">&quot;bcg_matrix&quot;</span> </span><br><span class="line">  <span class="keyword">WHERE</span></span><br><span class="line">    <span class="string">&quot;feed&quot;</span> = <span class="string">&#x27;free&#x27;</span> </span><br><span class="line">    <span class="keyword">AND</span> <span class="string">&quot;category&quot;</span> = <span class="string">&#x27;5000&#x27;</span> </span><br><span class="line">    <span class="keyword">AND</span> <span class="string">&quot;ranking&quot;</span> &lt;= <span class="number">100</span> </span><br><span class="line">  <span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">    <span class="string">&quot;appid&quot;</span>,</span><br><span class="line">    <span class="string">&quot;name&quot;</span>,</span><br><span class="line">    <span class="string">&quot;date&quot;</span> </span><br><span class="line">  )</span><br></pre></td></tr></table></figure></p></li><li><p>使用 EXISTS 筛选出七天都在榜单的应用，计算第一天的排名、最后一天的排名、每日评论份额占比。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  <span class="string">&quot;appid&quot;</span>,</span><br><span class="line">  <span class="string">&quot;name&quot;</span>,</span><br><span class="line">  <span class="string">&quot;date&quot;</span>,</span><br><span class="line">  <span class="keyword">FIRST_VALUE</span> ( <span class="string">&quot;ranking&quot;</span> ) <span class="keyword">OVER</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> <span class="string">&quot;appid&quot;</span>, <span class="string">&quot;name&quot;</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="string">&quot;date&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;start_ranking&quot;</span>,</span><br><span class="line">  <span class="keyword">FIRST_VALUE</span> ( <span class="string">&quot;ranking&quot;</span> ) <span class="keyword">OVER</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> <span class="string">&quot;appid&quot;</span>, <span class="string">&quot;name&quot;</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="string">&quot;date&quot;</span> <span class="keyword">DESC</span> ) <span class="keyword">AS</span> <span class="string">&quot;end_ranking&quot;</span>,</span><br><span class="line">  <span class="string">&quot;comment_num&quot;</span> / <span class="keyword">SUM</span> ( <span class="string">&quot;comment_num&quot;</span> ) <span class="keyword">OVER</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> <span class="string">&quot;date&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;comment_ratio&quot;</span> <span class="comment">-- 每日份额占比=评论数/评论总数</span></span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  t1 </span><br><span class="line"><span class="keyword">WHERE</span></span><br><span class="line">  <span class="keyword">EXISTS</span> ( <span class="comment">-- 七天都在榜单上</span></span><br><span class="line">  <span class="keyword">SELECT</span> <span class="string">&quot;appid&quot;</span> <span class="keyword">FROM</span> t1 <span class="keyword">AS</span> t2 <span class="keyword">WHERE</span> t1.appid = t2.appid <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="string">&quot;appid&quot;</span> <span class="keyword">HAVING</span> <span class="keyword">COUNT</span> ( <span class="number">1</span> ) = <span class="number">7</span> ) </span><br></pre></td></tr></table></figure></p></li><li><p>计算七天平均增长率和七天平均评论份额占比。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  <span class="string">&quot;appid&quot;</span>,</span><br><span class="line">  <span class="string">&quot;name&quot;</span>,</span><br><span class="line">  <span class="keyword">ROUND</span>( ( <span class="keyword">POWER</span> ( <span class="string">&quot;end_ranking&quot;</span> / <span class="string">&quot;start_ranking&quot;</span>, <span class="number">1.0</span> / <span class="number">6</span> ) - <span class="number">1</span> ) * <span class="number">100</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;change_ratio&quot;</span>,<span class="comment">-- 七天平均增长率，注意要用 1.0/6 而不能用 1/6</span></span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="keyword">AVG</span> ( <span class="string">&quot;comment_ratio&quot;</span> ) * <span class="number">100</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;comment_ratio&quot;</span> <span class="comment">-- 评论占比</span></span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  (...) t3</span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;appid&quot;</span>,</span><br><span class="line">  <span class="string">&quot;name&quot;</span>,</span><br><span class="line">  <span class="string">&quot;change_ratio&quot;</span></span><br></pre></td></tr></table></figure></p></li><li><p>对应用进行分类，这里以 <code>change_ratio = 0</code> 和 <code>AVG(comment_ratio)</code> 为分割线将应用分为四类。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  *,</span><br><span class="line">  <span class="keyword">CASE</span></span><br><span class="line">    <span class="keyword">WHEN</span> <span class="string">&quot;change_ratio&quot;</span> &gt;= <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;comment_ratio&quot;</span> &gt; <span class="keyword">AVG</span> ( <span class="string">&quot;comment_ratio&quot;</span> ) <span class="keyword">OVER</span> ( ) <span class="keyword">THEN</span> <span class="string">&#x27;明星&#x27;</span> </span><br><span class="line">    <span class="keyword">WHEN</span> <span class="string">&quot;change_ratio&quot;</span> &gt;= <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;comment_ratio&quot;</span> &lt; <span class="keyword">AVG</span> ( <span class="string">&quot;comment_ratio&quot;</span> ) <span class="keyword">OVER</span> ( ) <span class="keyword">THEN</span> <span class="string">&#x27;问题&#x27;</span> </span><br><span class="line">    <span class="keyword">WHEN</span> <span class="string">&quot;change_ratio&quot;</span> &lt; <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;comment_ratio&quot;</span> &gt; <span class="keyword">AVG</span> ( <span class="string">&quot;comment_ratio&quot;</span> ) <span class="keyword">OVER</span> ( ) <span class="keyword">THEN</span> <span class="string">&#x27;金牛&#x27;</span> </span><br><span class="line">    <span class="keyword">WHEN</span> <span class="string">&quot;change_ratio&quot;</span> &lt; <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;comment_ratio&quot;</span> &lt; <span class="keyword">AVG</span> ( <span class="string">&quot;comment_ratio&quot;</span> ) <span class="keyword">OVER</span> ( ) <span class="keyword">THEN</span> <span class="string">&#x27;瘦狗&#x27;</span> </span><br><span class="line">  <span class="keyword">END</span> <span class="keyword">AS</span> <span class="string">&quot;cls&quot;</span> </span><br><span class="line"><span class="keyword">FROM</span> (...) t4</span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;comment_ratio&quot;</span> <span class="keyword">DESC</span></span><br></pre></td></tr></table></figure></p></li></ol><p><strong>完整 SQL</strong><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">WITH</span> t1 <span class="keyword">AS</span> (</span><br><span class="line">  <span class="keyword">SELECT</span></span><br><span class="line">    <span class="string">&quot;appid&quot;</span>,</span><br><span class="line">    <span class="string">&quot;name&quot;</span>,</span><br><span class="line">    <span class="string">&quot;date&quot;</span>,</span><br><span class="line">    <span class="keyword">MIN</span> ( <span class="string">&quot;ranking&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;ranking&quot;</span>,</span><br><span class="line">    <span class="keyword">MAX</span> ( <span class="string">&quot;comment_num&quot;</span> ) <span class="string">&quot;comment_num&quot;</span> </span><br><span class="line">  <span class="keyword">FROM</span></span><br><span class="line">    <span class="string">&quot;bcg_matrix&quot;</span> </span><br><span class="line">  <span class="keyword">WHERE</span></span><br><span class="line">    <span class="string">&quot;feed&quot;</span> = <span class="string">&#x27;free&#x27;</span> </span><br><span class="line">    <span class="keyword">AND</span> <span class="string">&quot;category&quot;</span> = <span class="string">&#x27;5000&#x27;</span> </span><br><span class="line">    <span class="keyword">AND</span> <span class="string">&quot;ranking&quot;</span> &lt;= <span class="number">100</span> </span><br><span class="line">  <span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">    <span class="string">&quot;appid&quot;</span>,</span><br><span class="line">    <span class="string">&quot;name&quot;</span>,</span><br><span class="line">    <span class="string">&quot;date&quot;</span> </span><br><span class="line">  )</span><br><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  *,</span><br><span class="line">  <span class="keyword">CASE</span></span><br><span class="line">    <span class="keyword">WHEN</span> <span class="string">&quot;change_ratio&quot;</span> &gt;= <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;comment_ratio&quot;</span> &gt; <span class="keyword">AVG</span> ( <span class="string">&quot;comment_ratio&quot;</span> ) <span class="keyword">OVER</span> ( ) <span class="keyword">THEN</span> <span class="string">&#x27;明星&#x27;</span> </span><br><span class="line">    <span class="keyword">WHEN</span> <span class="string">&quot;change_ratio&quot;</span> &gt;= <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;comment_ratio&quot;</span> &lt; <span class="keyword">AVG</span> ( <span class="string">&quot;comment_ratio&quot;</span> ) <span class="keyword">OVER</span> ( ) <span class="keyword">THEN</span> <span class="string">&#x27;问题&#x27;</span> </span><br><span class="line">    <span class="keyword">WHEN</span> <span class="string">&quot;change_ratio&quot;</span> &lt; <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;comment_ratio&quot;</span> &gt; <span class="keyword">AVG</span> ( <span class="string">&quot;comment_ratio&quot;</span> ) <span class="keyword">OVER</span> ( ) <span class="keyword">THEN</span> <span class="string">&#x27;金牛&#x27;</span> </span><br><span class="line">    <span class="keyword">WHEN</span> <span class="string">&quot;change_ratio&quot;</span> &lt; <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;comment_ratio&quot;</span> &lt; <span class="keyword">AVG</span> ( <span class="string">&quot;comment_ratio&quot;</span> ) <span class="keyword">OVER</span> ( ) <span class="keyword">THEN</span> <span class="string">&#x27;瘦狗&#x27;</span> </span><br><span class="line">  <span class="keyword">END</span> <span class="keyword">AS</span> <span class="string">&quot;cls&quot;</span> </span><br><span class="line"><span class="keyword">FROM</span></span><br><span class="line">  (</span><br><span class="line">  <span class="keyword">SELECT</span></span><br><span class="line">    <span class="string">&quot;appid&quot;</span>,</span><br><span class="line">    <span class="string">&quot;name&quot;</span>,</span><br><span class="line">    <span class="keyword">ROUND</span>( ( <span class="keyword">POWER</span> ( <span class="string">&quot;end_ranking&quot;</span> / <span class="string">&quot;start_ranking&quot;</span>, <span class="number">1.0</span> / <span class="number">6</span> ) - <span class="number">1</span> ) * <span class="number">100</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;change_ratio&quot;</span>,<span class="comment">-- 七天平均增长率，注意要用 1.0/6 而不能用 1/6</span></span><br><span class="line">    <span class="keyword">ROUND</span>( <span class="keyword">AVG</span> ( <span class="string">&quot;comment_ratio&quot;</span> ) * <span class="number">100</span>, <span class="number">4</span> ) <span class="keyword">AS</span> <span class="string">&quot;comment_ratio&quot;</span> <span class="comment">-- 评论占比</span></span><br><span class="line">  <span class="keyword">FROM</span></span><br><span class="line">    (</span><br><span class="line">    <span class="keyword">SELECT</span></span><br><span class="line">      <span class="string">&quot;appid&quot;</span>,</span><br><span class="line">      <span class="string">&quot;name&quot;</span>,</span><br><span class="line">      <span class="string">&quot;date&quot;</span>,</span><br><span class="line">      <span class="keyword">FIRST_VALUE</span> ( <span class="string">&quot;ranking&quot;</span> ) <span class="keyword">OVER</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> <span class="string">&quot;appid&quot;</span>, <span class="string">&quot;name&quot;</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="string">&quot;date&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;start_ranking&quot;</span>,</span><br><span class="line">      <span class="keyword">FIRST_VALUE</span> ( <span class="string">&quot;ranking&quot;</span> ) <span class="keyword">OVER</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> <span class="string">&quot;appid&quot;</span>, <span class="string">&quot;name&quot;</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="string">&quot;date&quot;</span> <span class="keyword">DESC</span> ) <span class="keyword">AS</span> <span class="string">&quot;end_ranking&quot;</span>,</span><br><span class="line">      <span class="string">&quot;comment_num&quot;</span> / <span class="keyword">SUM</span> ( <span class="string">&quot;comment_num&quot;</span> ) <span class="keyword">OVER</span> ( <span class="keyword">PARTITION</span> <span class="keyword">BY</span> <span class="string">&quot;date&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;comment_ratio&quot;</span> <span class="comment">-- 每日份额占比=评论数/评论总数</span></span><br><span class="line">    <span class="keyword">FROM</span></span><br><span class="line">      t1 </span><br><span class="line">    <span class="keyword">WHERE</span></span><br><span class="line">      <span class="keyword">EXISTS</span> ( <span class="comment">-- 七天都在榜单上</span></span><br><span class="line">      <span class="keyword">SELECT</span> <span class="string">&quot;appid&quot;</span> <span class="keyword">FROM</span> t1 <span class="keyword">AS</span> t2 <span class="keyword">WHERE</span> t1.appid = t2.appid <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="string">&quot;appid&quot;</span> <span class="keyword">HAVING</span> <span class="keyword">COUNT</span> ( <span class="number">1</span> ) = <span class="number">7</span> ) </span><br><span class="line">    ) t3 </span><br><span class="line">  <span class="keyword">GROUP</span> <span class="keyword">BY</span></span><br><span class="line">    <span class="string">&quot;appid&quot;</span>,</span><br><span class="line">    <span class="string">&quot;name&quot;</span>,</span><br><span class="line">    <span class="string">&quot;change_ratio&quot;</span></span><br><span class="line">  ) t4</span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span></span><br><span class="line">  <span class="string">&quot;comment_ratio&quot;</span> <span class="keyword">DESC</span></span><br></pre></td></tr></table></figure></p><p><strong>输出数据</strong></p><div class="table-container"><table><thead><tr><th>appid</th><th>name</th><th>change_ratio</th><th>comment_ratio</th><th>cls</th></tr></thead><tbody><tr><td>1142110895</td><td>抖音短视频</td><td>0.0558</td><td>15.4006</td><td>明星</td></tr><tr><td>393765873</td><td>爱奇艺-奇葩说6独播</td><td>0.1248</td><td>8.6023</td><td>明星</td></tr><tr><td>458318329</td><td>腾讯视频-从前有座灵剑山热播</td><td>0.0642</td><td>8.3566</td><td>金牛</td></tr><tr><td>461703208</td><td>高德地图-精准地图,导航出行必备</td><td>0.0922</td><td>8.1495</td><td>明星</td></tr><tr><td>336141475</td><td>优酷视频-鹤唳华亭精彩呈现</td><td>0.1930</td><td>7.2290</td><td>明星</td></tr><tr><td>507161324</td><td>饿了么-外卖订餐，30分钟准时送达</td><td>0.0671</td><td>4.8019</td><td>明星</td></tr></tbody></table></div><h3 id="数据可视化"><a href="#数据可视化" class="headerlink" title="数据可视化"></a>数据可视化</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/bcg_matrix.png" alt="波士顿矩阵图"></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/">数据分析</category>
      
      <category domain="https://blog.eurkon.com/tags/PostgreSQL/">PostgreSQL</category>
      
      
      <comments>https://blog.eurkon.com/post/dd96e05f.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ABC 分类法</title>
      <link>https://blog.eurkon.com/post/3e7451d9.html</link>
      <guid>https://blog.eurkon.com/post/3e7451d9.html</guid>
      <pubDate>Sat, 15 Jan 2022 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;h3 id=&quot;模型概念&quot;&gt;&lt;a href=&quot;#模型概念&quot; class=&quot;headerlink&quot; title=&quot;模型概念&quot;&gt;&lt;/a&gt;模型概念&lt;/h</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><h3 id="模型概念"><a href="#模型概念" class="headerlink" title="模型概念"></a>模型概念</h3><p>ABC 分类法（Activity Based Classification），全称为 ABC 分类库存控制法。又称帕累托分析法或巴雷托分析法、柏拉图分析、主次因分析法 、ABC 分析法、分类管理法、物资重点管理法、ABC 管理法、巴雷特分析法，平常我们也称之为“二八定律”，在任何一组东西中，最重要的只占其中一小部分，其余尽管是多数，却是次要的。</p><p>ABC 分类法是项目管理中常用的一种方法，它是根据事物在技术或经济方面的主要特征，进行分类排队，分清重点和一般，从而有区别地确定管理方式的一种分析方法。由于它把被分析的对象分成 A、B、C 三类，所以又称为 ABC 分析法。</p><p>在 ABC 分类法的分析图中，有两个纵坐标，一个横坐标，几个长方形，一条曲线，左边纵坐标表示频数，右边纵坐标表示频率，以百分数表示。横坐标表示影响质量的各项因素，按影响大小从左向右排列，曲线表示各种影响因素大小的累计百分数。一般地，是将曲线的累计频率分为三级，与之相对应的因素分为三类：</p><ul><li>A 类因素：发生累计频率为 0~80，是主要影响因素。</li><li>B 类因素：发生累计频率为 80~90，是次要影响因素。</li><li>C 类因素：  发生累计频率为 90~100，是一般影响因素。</li></ul><h3 id="应用意义"><a href="#应用意义" class="headerlink" title="应用意义"></a>应用意义</h3><p>社会上任何复杂事物，都存在着“关键的少数和一般的多数”这样一种规律。事物越是复杂，这一规律便越是显著。如果将有限的力量主要（重点）用于解决这具有决定性影响的少数事物上，和将有限力量平均分摊在全部事物上。两者比较，当然是前者可以取得较好的成效，而后者成效较差。ABC 分类便是在这一思想的指导下，通过分析，将“关键的少数”找出来，并确定与之适应的管理方法，这便形成了要进行重点管理的 A 类事物。</p><p>ABC 分类更多地应用于仓储和物流管理中，在一个大型公司中，库存存货的种类通常会很多，动则就可能是十几万种甚至几十万种。ABC分析的应用，在储存管理中比较容易地取得以下成效：第一，压缩了总库存量；第二，解放了被占压的资金；第三，使库存结构合理化；第四，节约了管理力量。这一法则的运用就可以使工作效率和效益大大提高。</p><h2 id="实战"><a href="#实战" class="headerlink" title="实战"></a>实战</h2><h3 id="基础数据"><a href="#基础数据" class="headerlink" title="基础数据"></a>基础数据</h3><p>这里选择 电子游戏销售数据进行分析，数据字段为：</p><ul><li>Rank：总销售量排名</li><li>Name：游戏名称</li><li>Platform：游戏发布平台（即 PC、PS4 等）</li><li>Year：游戏发行年份</li><li>Genre：游戏类型</li><li>Publisher：游戏发行商</li><li>NA_Sales：北美销售量（百万）</li><li>EU_Sales：在欧洲的销售量（百万）</li><li>JP_Sales：日本的销售量（百万）</li><li>Other_Sales：世界其他地区的销售量（百万）</li><li>Global_Sales：全球总销售量</li></ul><p>部分数据如下：</p><div class="table-container"><table><thead><tr><th>Rank</th><th>Name</th><th>Platform</th><th>Year</th><th>Genre</th><th>Publisher</th><th>NA_Sales</th><th>EU_Sales</th><th>JP_Sales</th><th>Other_Sales</th><th>Global_Sales</th></tr></thead><tbody><tr><td>1</td><td>Wii Sports</td><td>Wii</td><td>2006</td><td>Sports</td><td>Nintendo</td><td>41.49</td><td>29.02</td><td>3.77</td><td>8.46</td><td>82.74</td></tr><tr><td>2</td><td>Super Mario Bros.</td><td>NES</td><td>1985</td><td>Platform</td><td>Nintendo</td><td>29.08</td><td>3.58</td><td>6.81</td><td>0.77</td><td>40.24</td></tr><tr><td>3</td><td>Mario Kart Wii</td><td>Wii</td><td>2008</td><td>Racing</td><td>Nintendo</td><td>15.85</td><td>12.88</td><td>3.79</td><td>3.31</td><td>35.82</td></tr><tr><td>4</td><td>Wii Sports Resort</td><td>Wii</td><td>2009</td><td>Sports</td><td>Nintendo</td><td>15.75</td><td>11.01</td><td>3.28</td><td>2.96</td><td>33.00</td></tr><tr><td>5</td><td>Pokemon Red/Pokemon Blue</td><td>GB</td><td>1996</td><td>Role-Playing</td><td>Nintendo</td><td>11.27</td><td>8.89</td><td>10.22</td><td>1.00</td><td>31.37</td></tr><tr><td>6</td><td>Tetris</td><td>GB</td><td>1989</td><td>Puzzle</td><td>Nintendo</td><td>23.20</td><td>2.26</td><td>4.22</td><td>0.58</td><td>30.26</td></tr></tbody></table></div><p>数据下载地址：<a href="https://www.kaggle.com/datasets/gregorut/videogamesales">Video Game Sales</a></p><h3 id="数据处理"><a href="#数据处理" class="headerlink" title="数据处理"></a>数据处理</h3><p>由于数据较多，下面只计算 2016 年 PC 平台游戏按销量排序后的累计销售量和占比等数据：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">  <span class="string">&quot;name&quot;</span>,</span><br><span class="line">  <span class="string">&quot;global_sales&quot;</span>, <span class="comment">-- 销售量</span></span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="string">&quot;global_sales&quot;</span> / <span class="keyword">SUM</span> ( <span class="string">&quot;global_sales&quot;</span> ) <span class="keyword">OVER</span> ( ) * <span class="number">100</span>, <span class="number">2</span> ) <span class="keyword">AS</span> <span class="string">&quot;pct_sales&quot;</span>, <span class="comment">-- 百分比</span></span><br><span class="line">  <span class="keyword">SUM</span> ( <span class="string">&quot;global_sales&quot;</span> ) <span class="keyword">OVER</span> ( <span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="string">&quot;global_sales&quot;</span> <span class="keyword">DESC</span> <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="keyword">UNBOUNDED</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span> ) <span class="keyword">AS</span> <span class="string">&quot;csum_global_sales&quot;</span>, <span class="comment">-- 累计销售量</span></span><br><span class="line">  <span class="keyword">ROUND</span>( <span class="keyword">SUM</span> ( <span class="string">&quot;global_sales&quot;</span> ) <span class="keyword">OVER</span> ( <span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="string">&quot;global_sales&quot;</span> <span class="keyword">DESC</span> <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="keyword">UNBOUNDED</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span> ) / <span class="keyword">SUM</span> ( <span class="string">&quot;global_sales&quot;</span> ) <span class="keyword">OVER</span> ( ) * <span class="number">100</span>, <span class="number">2</span> ) <span class="keyword">AS</span> <span class="string">&quot;pct_csum_sales&quot;</span> <span class="comment">-- 累计销售量百分比</span></span><br><span class="line"><span class="keyword">FROM</span> (</span><br><span class="line">    <span class="keyword">SELECT</span></span><br><span class="line">      <span class="string">&quot;name&quot;</span>,</span><br><span class="line">      <span class="keyword">SUM</span> ( <span class="string">&quot;global_sales&quot;</span> ) <span class="keyword">AS</span> <span class="string">&quot;global_sales&quot;</span></span><br><span class="line">    <span class="keyword">FROM</span> <span class="string">&quot;abc&quot;</span></span><br><span class="line">    <span class="keyword">WHERE</span> <span class="string">&quot;year&quot;</span> = <span class="string">&#x27;2016&#x27;</span></span><br><span class="line">    <span class="keyword">AND</span> <span class="string">&quot;platform&quot;</span> = <span class="string">&#x27;PC&#x27;</span></span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="string">&quot;name&quot;</span>) t1</span><br></pre></td></tr></table></figure></p><p><strong>输出数据</strong></p><div class="table-container"><table><thead><tr><th>name</th><th>global_sales</th><th>pct_global_sales</th><th>csum_sales</th><th>pct_csum_sales</th></tr></thead><tbody><tr><td>Overwatch</td><td>0.43</td><td>16.54</td><td>0.43</td><td>16.54</td></tr><tr><td>Tom Clancy&#39;s The Division</td><td>0.37</td><td>14.23</td><td>0.80</td><td>30.77</td></tr><tr><td>World of Warcraft: Legion</td><td>0.29</td><td>11.15</td><td>1.09</td><td>41.92</td></tr><tr><td>XCOM 2</td><td>0.20</td><td>7.69</td><td>1.29</td><td>49.62</td></tr><tr><td>Doom (2016)</td><td>0.18</td><td>6.92</td><td>1.47</td><td>56.54</td></tr><tr><td>Far Cry: Primal</td><td>0.14</td><td>5.38</td><td>1.61</td><td>61.92</td></tr><tr><td>Total War: WARHAMMER</td><td>0.10</td><td>3.85</td><td>1.71</td><td>65.77</td></tr><tr><td>Rise of the Tomb Raider</td><td>0.10</td><td>3.85</td><td>1.81</td><td>69.62</td></tr></tbody></table></div><h3 id="数据可视化"><a href="#数据可视化" class="headerlink" title="数据可视化"></a>数据可视化</h3><p>根据 ABC 分析法，将游戏按销售量降序排列，依次分成销售量占比为 80%，10%，10% 对应 A 类，B 类，C 类三类，用不同颜色的柱形图展示出来。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/abc_pareto.png" alt="ABC 分类法 - 帕累托图"></p><p>可以看出前面 13 个游戏的累计销售量达到了 <code>80%</code>，前 21 个游戏的累计销售量达到了 <code>90%</code>，所以我们可以得出 ABC 类游戏的分类情况如下：</p><div class="table-container"><table><thead><tr><th>类别</th><th>名称</th><th>数量占比</th><th>销售量占比</th></tr></thead><tbody><tr><td>A 类</td><td>Overwatch、Tom Clancy&#39;s The Division、World of Warcraft: Legion、<br>XCOM 2、Doom (2016)、Far Cry: Primal、Total War: WARHAMMER、<br>Rise of the Tomb Raider、Battleborn、Plants vs. Zombies: Garden Warfare 2<br>、FIFA 17、Mirror&#39;s Edge Catalyst、Dark Souls III</td><td>13</td><td>80%</td></tr><tr><td>B 类</td><td>Street Fighter V、Life is Strange、DiRT Rally、Pro Cycling Manager 2016、<br>Hearts of Iron IV、Stellaris、Need for Speed (2015)、<br>Agatha Christie: The ABC Murders</td><td>8</td><td>10%</td></tr><tr><td>C 类</td><td>Deus Ex: Mankind Divided、F1 2016 (Codemasters)、<br>Homefront: The Revolution、Bus Simulator 16、Resident Evil Zero、<br>Revolution: 25th Anniversary Collection、Rocket League、<br>RollerCoaster Tycoon World、Song of the Deep、The Technomancer、<br>Battle Worlds: Kronos、Total War Attila: Tyrants &amp; Kings、MXGP 2、<br>TrackMania Turbo、Valentino Rossi: The Game、<br>Codename: Panzers Complete Collection、Cities: Skylines Snowfall</td><td>17</td><td>10%</td></tr></tbody></table></div>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/">数据分析</category>
      
      <category domain="https://blog.eurkon.com/tags/PostgreSQL/">PostgreSQL</category>
      
      
      <comments>https://blog.eurkon.com/post/3e7451d9.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>RFM 客户分析模型</title>
      <link>https://blog.eurkon.com/post/97f6661d.html</link>
      <guid>https://blog.eurkon.com/post/97f6661d.html</guid>
      <pubDate>Sat, 01 Jan 2022 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;h3 id=&quot;模型概念&quot;&gt;&lt;a href=&quot;#模型概念&quot; class=&quot;headerlink&quot; title=&quot;模型概念&quot;&gt;&lt;/a&gt;模型概念&lt;/h</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><h3 id="模型概念"><a href="#模型概念" class="headerlink" title="模型概念"></a>模型概念</h3><p>RFM 模型是衡量客户价值和客户创造利益能力的重要工具和手段。根据美国数据库营销研究所 Arthur Hughes 的研究，客户数据库中有 3 个神奇的要素：</p><ul><li>最近一次消费时间（Recency）：客户距离最近的一次消费时间的间隔。</li><li>最近一段时间内消费频次（Frequency）：指客户在限定的期间内所消费购买的次数。</li><li>最近一段时间内消费金额（Monetary）：客户的消费能力，通常以客户单次的平均消费金额作为衡量指标。</li></ul><p>这 3 个要素构成了数据分析最好的指标。</p><p>得到客户的特征向量值后，在 R、F、M 任意一项中的价值可被分为高（<code>1</code>）、低（<code>0</code>）两类，综合 R、F、M 三项的表现，用户可被划分为 8 种类型，详细类型及分类规则如下表所示：</p><div class="table-container"><table><thead><tr><th>客户类型</th><th>客户价值</th><th>分类说明</th></tr></thead><tbody><tr><td>重要价值客户</td><td><code>(1 1 1)</code></td><td>最近消费时间近、消费频次和消费金额都很高（VIP）</td></tr><tr><td>重要发展客户</td><td><code>(1 0 1)</code></td><td>最近消费时间较近、消费金额高，但频次不高，忠诚度不高，<br>很有潜力的用户，必须重点发展。</td></tr><tr><td>重要保持客户</td><td><code>(0 1 1)</code></td><td>最近消费时间较远，消费金额和频次都很高。</td></tr><tr><td>重要挽留客户</td><td><code>(0 0 1)</code></td><td>最近消费时间较远、消费频次不高，但消费金额高的用户，<br>可能是将要流失或者已经要流失的用户。</td></tr><tr><td>一般价值客户</td><td><code>(1 1 0)</code></td><td>最近消费时间近，频率高但消费金额低，需要提高其客单价。</td></tr><tr><td>一般发展客户</td><td><code>(1 0 0)</code></td><td>最近消费时间较近、消费金额，频次都不高。</td></tr><tr><td>一般保持客户</td><td><code>(0 1 0)</code></td><td>最近消费时间较远、消费频次高，但金额不高。</td></tr><tr><td>一般挽留客户</td><td><code>(0 0 0)</code></td><td>三个关键指标都不高。</td></tr></tbody></table></div><h3 id="应用意义"><a href="#应用意义" class="headerlink" title="应用意义"></a>应用意义</h3><p>RFM 分析就是通过三个关键指标对客户进行观察和分类，判断每类细分用户的价值。针对不同的特征的客户进行相应的营销策略。</p><p>RFM 非常适用于生产多种商品的企业，而且这些商品单价相对不高，如消费品、化妆品、小家电、录像带店、超市等；它也适合在一个企业内只有少数耐久商品，但是该商品中有一部分属于消耗品，如复印机、打印机、汽车维修等消耗品；RFM 对于加油站、旅行保险、运输、快递、快餐店、KTV、行动电话信用卡、证券公司等也很适合。</p><h2 id="实战"><a href="#实战" class="headerlink" title="实战"></a>实战</h2><h3 id="基础数据"><a href="#基础数据" class="headerlink" title="基础数据"></a>基础数据</h3><p>这里只选择最基本的三个字段进行分析，数据字段为：</p><ul><li>CardID：客户</li><li>TrxDate：消费时间</li><li>Amount：消费金额</li></ul><p>部分数据如下：</p><div class="table-container"><table><thead><tr><th>CardID</th><th>TrxDate</th><th>Amount</th></tr></thead><tbody><tr><td>1000101</td><td>2019-01-09</td><td>77035</td></tr><tr><td>1000101</td><td>2019-11-06</td><td>98946</td></tr><tr><td>1000102</td><td>2020-03-05</td><td>178002</td></tr><tr><td>1000102</td><td>2020-06-03</td><td>381420</td></tr><tr><td>1000102</td><td>2019-01-05</td><td>152999</td></tr><tr><td>1000102</td><td>2020-01-02</td><td>256770</td></tr><tr><td>1000103</td><td>2019-01-02</td><td>31538</td></tr><tr><td>1000103</td><td>2019-06-07</td><td>215572</td></tr><tr><td>1000103</td><td>2019-01-07</td><td>27906</td></tr><tr><td>1000103</td><td>2019-01-05</td><td>65549</td></tr></tbody></table></div><p>数据下载地址：<a href="https://www.kaggle.com/datasets/luckyrachmatsaputra/rfm-uas">rfm uas</a></p><h3 id="数据处理"><a href="#数据处理" class="headerlink" title="数据处理"></a>数据处理</h3><ol><li><p>计算 RFM 三个字段：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">RIGHT</span>(<span class="string">&quot;card_id&quot;</span>, <span class="number">3</span>) <span class="keyword">AS</span> <span class="string">&quot;card_id&quot;</span>,</span><br><span class="line">  <span class="keyword">CURRENT_DATE</span> - <span class="keyword">MAX</span>(<span class="string">&quot;trx_date&quot;</span>) <span class="keyword">AS</span> <span class="string">&quot;r&quot;</span>, <span class="comment">-- 距离最近的一次消费时间的间隔</span></span><br><span class="line">  <span class="keyword">COUNT</span>(<span class="string">&quot;card_id&quot;</span>) <span class="keyword">AS</span> <span class="string">&quot;f&quot;</span>, <span class="comment">-- 消费频率</span></span><br><span class="line">  <span class="keyword">ROUND</span>(<span class="keyword">AVG</span>(<span class="string">&quot;amount&quot;</span>), <span class="number">4</span>) <span class="keyword">AS</span> <span class="string">&quot;m&quot;</span> <span class="comment">-- 单次的平均消费金额</span></span><br><span class="line"><span class="keyword">FROM</span> <span class="string">&quot;rfm&quot;</span></span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="string">&quot;card_id&quot;</span></span><br></pre></td></tr></table></figure></p></li><li><p>构造客户 RFM 特征向量值，这里通过判断是否大于均值去构造特征向量值，也可以使用标准化后的数据作为特征向量值，再使用 KMeans 等聚类方法将客户分为 8 个类别，再自行区分客户价值类型。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> *,</span><br><span class="line">    <span class="keyword">CASE</span> <span class="keyword">WHEN</span> (<span class="string">&quot;r&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;r&quot;</span>) <span class="keyword">OVER</span>()) &lt; <span class="number">0</span> <span class="keyword">THEN</span> <span class="number">1</span> <span class="keyword">ELSE</span> <span class="number">0</span> <span class="keyword">END</span> <span class="keyword">AS</span> <span class="string">&quot;vr&quot;</span>, <span class="comment">-- 时间间隔小于平均值则 1</span></span><br><span class="line">    <span class="keyword">CASE</span> <span class="keyword">WHEN</span> (<span class="string">&quot;f&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;f&quot;</span>) <span class="keyword">OVER</span>()) &gt; <span class="number">0</span> <span class="keyword">THEN</span> <span class="number">1</span> <span class="keyword">ELSE</span> <span class="number">0</span> <span class="keyword">END</span> <span class="keyword">AS</span> <span class="string">&quot;vf&quot;</span>, <span class="comment">-- 消费频率大于平均值则 1</span></span><br><span class="line">    <span class="keyword">CASE</span> <span class="keyword">WHEN</span> (<span class="string">&quot;m&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;m&quot;</span>) <span class="keyword">OVER</span>()) &gt; <span class="number">0</span> <span class="keyword">THEN</span> <span class="number">1</span> <span class="keyword">ELSE</span> <span class="number">0</span> <span class="keyword">END</span> <span class="keyword">AS</span> <span class="string">&quot;vm&quot;</span>, <span class="comment">-- 平均消费金额大于平均值则 1</span></span><br><span class="line">    <span class="keyword">ROUND</span>((<span class="string">&quot;r&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;r&quot;</span>) <span class="keyword">OVER</span>()) / <span class="keyword">STDDEV</span>(<span class="string">&quot;r&quot;</span>) <span class="keyword">OVER</span>(), <span class="number">4</span>) <span class="keyword">AS</span> <span class="string">&quot;zr&quot;</span>, <span class="comment">-- 标准化</span></span><br><span class="line">    <span class="keyword">ROUND</span>((<span class="string">&quot;f&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;f&quot;</span>) <span class="keyword">OVER</span>()) / <span class="keyword">STDDEV</span>(<span class="string">&quot;f&quot;</span>) <span class="keyword">OVER</span>(), <span class="number">4</span>) <span class="keyword">AS</span> <span class="string">&quot;zf&quot;</span>, <span class="comment">-- 标准化</span></span><br><span class="line">    <span class="keyword">ROUND</span>((<span class="string">&quot;m&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;m&quot;</span>) <span class="keyword">OVER</span>()) / <span class="keyword">STDDEV</span>(<span class="string">&quot;m&quot;</span>) <span class="keyword">OVER</span>(), <span class="number">4</span>) <span class="keyword">AS</span> <span class="string">&quot;zm&quot;</span> <span class="comment">-- 标准化</span></span><br><span class="line">  <span class="keyword">FROM</span> ( ... ) t1</span><br></pre></td></tr></table></figure></p></li><li><p>如果是用均值构造特征向量值，则可继续使用 SQL 进行客户价值分类，如果使用分类模型，则可导出数据到 Python 等处理。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> *,</span><br><span class="line">    <span class="keyword">CASE</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">1</span> <span class="keyword">THEN</span> <span class="string">&#x27;重要价值客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">1</span> <span class="keyword">THEN</span> <span class="string">&#x27;重要发展客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">1</span> <span class="keyword">THEN</span> <span class="string">&#x27;重要保持客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">1</span> <span class="keyword">THEN</span> <span class="string">&#x27;重要挽留客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">0</span> <span class="keyword">THEN</span> <span class="string">&#x27;一般价值客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">0</span> <span class="keyword">THEN</span> <span class="string">&#x27;一般发展客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">0</span> <span class="keyword">THEN</span> <span class="string">&#x27;一般保持客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">0</span> <span class="keyword">THEN</span> <span class="string">&#x27;一般挽留客户&#x27;</span></span><br><span class="line">      <span class="keyword">ELSE</span> <span class="literal">NULL</span></span><br><span class="line">    <span class="keyword">END</span> <span class="keyword">AS</span> cls</span><br><span class="line">  <span class="keyword">FROM</span> (...) t2</span><br></pre></td></tr></table></figure></p></li></ol><p><strong>完整 SQL:</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> *,</span><br><span class="line">    <span class="keyword">CASE</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">1</span> <span class="keyword">THEN</span> <span class="string">&#x27;重要价值客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">1</span> <span class="keyword">THEN</span> <span class="string">&#x27;重要发展客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">1</span> <span class="keyword">THEN</span> <span class="string">&#x27;重要保持客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">1</span> <span class="keyword">THEN</span> <span class="string">&#x27;重要挽留客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">0</span> <span class="keyword">THEN</span> <span class="string">&#x27;一般价值客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">0</span> <span class="keyword">THEN</span> <span class="string">&#x27;一般发展客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">1</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">0</span> <span class="keyword">THEN</span> <span class="string">&#x27;一般保持客户&#x27;</span></span><br><span class="line">      <span class="keyword">WHEN</span> <span class="string">&quot;vr&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vf&quot;</span> = <span class="number">0</span> <span class="keyword">AND</span> <span class="string">&quot;vm&quot;</span> = <span class="number">0</span> <span class="keyword">THEN</span> <span class="string">&#x27;一般挽留客户&#x27;</span></span><br><span class="line">      <span class="keyword">ELSE</span> <span class="literal">NULL</span></span><br><span class="line">    <span class="keyword">END</span> <span class="keyword">AS</span> cls</span><br><span class="line">  <span class="keyword">FROM</span> (</span><br><span class="line">        <span class="keyword">SELECT</span> *,</span><br><span class="line">            <span class="keyword">CASE</span> <span class="keyword">WHEN</span> (<span class="string">&quot;r&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;r&quot;</span>) <span class="keyword">OVER</span>()) &lt; <span class="number">0</span> <span class="keyword">THEN</span> <span class="number">1</span> <span class="keyword">ELSE</span> <span class="number">0</span> <span class="keyword">END</span> <span class="keyword">AS</span> <span class="string">&quot;vr&quot;</span>, <span class="comment">-- 时间间隔小于平均值则 1</span></span><br><span class="line">            <span class="keyword">CASE</span> <span class="keyword">WHEN</span> (<span class="string">&quot;f&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;f&quot;</span>) <span class="keyword">OVER</span>()) &gt; <span class="number">0</span> <span class="keyword">THEN</span> <span class="number">1</span> <span class="keyword">ELSE</span> <span class="number">0</span> <span class="keyword">END</span> <span class="keyword">AS</span> <span class="string">&quot;vf&quot;</span>, <span class="comment">-- 消费频率大于平均值则 1</span></span><br><span class="line">            <span class="keyword">CASE</span> <span class="keyword">WHEN</span> (<span class="string">&quot;m&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;m&quot;</span>) <span class="keyword">OVER</span>()) &gt; <span class="number">0</span> <span class="keyword">THEN</span> <span class="number">1</span> <span class="keyword">ELSE</span> <span class="number">0</span> <span class="keyword">END</span> <span class="keyword">AS</span> <span class="string">&quot;vm&quot;</span>, <span class="comment">-- 平均消费金额大于平均值则 1</span></span><br><span class="line">            <span class="keyword">ROUND</span>((<span class="string">&quot;r&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;r&quot;</span>) <span class="keyword">OVER</span>()) / <span class="keyword">STDDEV</span>(<span class="string">&quot;r&quot;</span>) <span class="keyword">OVER</span>(), <span class="number">4</span>) <span class="keyword">AS</span> <span class="string">&quot;zr&quot;</span>, <span class="comment">-- 标准化</span></span><br><span class="line">            <span class="keyword">ROUND</span>((<span class="string">&quot;f&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;f&quot;</span>) <span class="keyword">OVER</span>()) / <span class="keyword">STDDEV</span>(<span class="string">&quot;f&quot;</span>) <span class="keyword">OVER</span>(), <span class="number">4</span>) <span class="keyword">AS</span> <span class="string">&quot;zf&quot;</span>, <span class="comment">-- 标准化</span></span><br><span class="line">            <span class="keyword">ROUND</span>((<span class="string">&quot;m&quot;</span> - <span class="keyword">AVG</span>(<span class="string">&quot;m&quot;</span>) <span class="keyword">OVER</span>()) / <span class="keyword">STDDEV</span>(<span class="string">&quot;m&quot;</span>) <span class="keyword">OVER</span>(), <span class="number">4</span>) <span class="keyword">AS</span> <span class="string">&quot;zm&quot;</span> <span class="comment">-- 标准化</span></span><br><span class="line">          <span class="keyword">FROM</span> (</span><br><span class="line">                <span class="keyword">SELECT</span> <span class="keyword">RIGHT</span>(<span class="string">&quot;card_id&quot;</span>, <span class="number">3</span>) <span class="keyword">AS</span> <span class="string">&quot;card_id&quot;</span>,</span><br><span class="line">                    <span class="keyword">CURRENT_DATE</span> - <span class="keyword">MAX</span>(<span class="string">&quot;trx_date&quot;</span>) <span class="keyword">AS</span> <span class="string">&quot;r&quot;</span>, <span class="comment">-- 距离最近的一次消费时间的间隔</span></span><br><span class="line">                    <span class="keyword">COUNT</span>(<span class="string">&quot;card_id&quot;</span>) <span class="keyword">AS</span> <span class="string">&quot;f&quot;</span>, <span class="comment">-- 消费频率</span></span><br><span class="line">                    <span class="keyword">ROUND</span>(<span class="keyword">AVG</span>(<span class="string">&quot;amount&quot;</span>), <span class="number">4</span>) <span class="keyword">AS</span> <span class="string">&quot;m&quot;</span> <span class="comment">-- 单次的平均消费金额</span></span><br><span class="line">                  <span class="keyword">FROM</span> <span class="string">&quot;rfm&quot;</span></span><br><span class="line">                <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="string">&quot;card_id&quot;</span>) t1</span><br><span class="line">        ) t2</span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="string">&quot;card_id&quot;</span></span><br></pre></td></tr></table></figure></p><p><strong>输出数据</strong></p><div class="table-container"><table><thead><tr><th>card_id</th><th>r</th><th>f</th><th>m</th><th>vr</th><th>vf</th><th>vm</th><th>zr</th><th>zf</th><th>zm</th><th>cls</th></tr></thead><tbody><tr><td>101</td><td>891</td><td>2</td><td>87990.5000</td><td>0</td><td>0</td><td>0</td><td>0.3689</td><td>-0.5642</td><td>-1.3177</td><td>一般挽留客户</td></tr><tr><td>102</td><td>681</td><td>4</td><td>242297.7500</td><td>1</td><td>1</td><td>1</td><td>-0.6917</td><td>0.6538</td><td>0.5175</td><td>重要价值客户</td></tr><tr><td>103</td><td>1043</td><td>4</td><td>85141.2500</td><td>0</td><td>0</td><td>1</td><td>1.1365</td><td>0.6538</td><td>-1.3516</td><td>重要挽留客户</td></tr><tr><td>104</td><td>1200</td><td>1</td><td>231207.0000</td><td>0</td><td>1</td><td>0</td><td>1.9294</td><td>-1.1733</td><td>0.3856</td><td>一般保持客户</td></tr><tr><td>105</td><td>801</td><td>7</td><td>188911.8571</td><td>1</td><td>0</td><td>1</td><td>-0.0856</td><td>2.4809</td><td>-0.1175</td><td>重要发展客户</td></tr><tr><td>106</td><td>832</td><td>5</td><td>275764.2000</td><td>0</td><td>1</td><td>1</td><td>0.0709</td><td>1.2628</td><td>0.9155</td><td>重要保持客户</td></tr><tr><td>107</td><td>618</td><td>1</td><td>348289.0000</td><td>1</td><td>1</td><td>0</td><td>-1.0098</td><td>-1.1733</td><td>1.7780</td><td>一般价值客户</td></tr></tbody></table></div><h3 id="数据可视化"><a href="#数据可视化" class="headerlink" title="数据可视化"></a>数据可视化</h3><ol><li><p>绘制 R-客户 的散点图，其中 X 轴为客户，Y 轴为距离最后一次消费的时间间隔 R，其中 R 特征值为 1（R &lt; 平均值）的为价值用户（1 1 ?）和发展用户（1 0 ?）。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/rfm_r.png" alt="R-客户"></p></li><li><p>绘制 F-客户 的散点图，其中 X 轴为客户，Y 轴为客户消费频次，其中 F 特征值为 1（F &gt; 平均值）的为价值用户（1 1 ?）和发展用户（0 1 ?）保持用户。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/rfm_f.png" alt="F-客户"></p></li><li><p>绘制 M-客户 的散点图，其中 X 轴为客户，Y 轴为客户单次平均消费金额，其中 M 特征值为 1（M &gt; 平均值）的为重要用户（1 ? ?）。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/rfm_m.png" alt="M-客户"></p></li><li><p>绘制 RFM 模型气泡图，其中 X 轴为消费频率 F，Y 轴为消费金额 M，气泡大小为距离最后一次消费的时间间隔 R（气泡越大说明时间间隔越长，客户价值越低），气泡颜色各个客户分类。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="../images/post/data_analyze/rfm_bubble.png" alt="RFM模型气泡图"></p></li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/">数据分析</category>
      
      <category domain="https://blog.eurkon.com/tags/PostgreSQL/">PostgreSQL</category>
      
      
      <comments>https://blog.eurkon.com/post/97f6661d.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>JavaScript 实用技巧</title>
      <link>https://blog.eurkon.com/post/ea69450.html</link>
      <guid>https://blog.eurkon.com/post/ea69450.html</guid>
      <pubDate>Sat, 25 Dec 2021 02:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;字符串&quot;&gt;&lt;a href=&quot;#字符串&quot; class=&quot;headerlink&quot; title=&quot;字符串&quot;&gt;&lt;/a&gt;字符串&lt;/h2&gt;&lt;h3 id=&quot;生成随机-ID&quot;&gt;&lt;a href=&quot;#生成随机-ID&quot; class=&quot;headerlink&quot; title=&quot;生成随机 ID</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h2><h3 id="生成随机-ID"><a href="#生成随机-ID" class="headerlink" title="生成随机 ID"></a>生成随机 ID</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Math</span>.random().toString(<span class="number">36</span>).substr(<span class="number">2</span>);</span><br><span class="line"><span class="comment">// =&gt; &quot;l10buc9ga8&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="字符串翻转"><a href="#字符串翻转" class="headerlink" title="字符串翻转"></a>字符串翻转</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&#x27;hello world&#x27;</span>.split(<span class="string">&#x27;&#x27;</span>).reverse().join(<span class="string">&#x27;&#x27;</span>);</span><br><span class="line"><span class="comment">// =&gt; &#x27;dlrow olleh&#x27;</span></span><br></pre></td></tr></table></figure></p><h3 id="从-HTML-中获取内容"><a href="#从-HTML-中获取内容" class="headerlink" title="从 HTML 中获取内容"></a>从 HTML 中获取内容</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getTextInHTML</span> (<span class="params">html</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> DOMParser().parseFromString(html, <span class="string">&#x27;text/html&#x27;</span>).body.textContent || <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line">getTextInHTML(<span class="string">&#x27;&lt;h2&gt;Hello World&lt;/h2&gt;&#x27;</span>);</span><br><span class="line"><span class="comment">// =&gt; &#x27;Hello World&#x27;</span></span><br></pre></td></tr></table></figure></p><h2 id="数字"><a href="#数字" class="headerlink" title="数字"></a>数字</h2><h3 id="转数"><a href="#转数" class="headerlink" title="转数"></a>转数</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 仅对 null、&#x27;&#x27;、false、数字字符串有效</span></span><br><span class="line"><span class="keyword">let</span> num1 = +<span class="literal">null</span>;</span><br><span class="line"><span class="keyword">let</span> num2 = +<span class="string">&#x27;&#x27;</span>;</span><br><span class="line"><span class="keyword">let</span> num3 = +<span class="literal">false</span>;</span><br><span class="line"><span class="keyword">let</span> num4 = +<span class="string">&#x27;123&#x27;</span>;</span><br><span class="line"><span class="keyword">let</span> num5 = +<span class="keyword">new</span> <span class="built_in">Date</span>(<span class="string">&#x27;2021-12-25&#x27;</span>);</span><br><span class="line"><span class="comment">// num1 num2 num3 num4 num5 =&gt; 0 0 0 123 1640390400000</span></span><br></pre></td></tr></table></figure></p><h3 id="直接取整"><a href="#直接取整" class="headerlink" title="直接取整"></a>直接取整</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 可用于字符串</span></span><br><span class="line"><span class="keyword">let</span> num1 = ~~ <span class="number">1.09</span>;</span><br><span class="line"><span class="keyword">let</span> num2 = <span class="number">2.19</span> | <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> num3 = <span class="number">3.29</span> &gt;&gt; <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> num3 = <span class="number">4.39</span> ^ <span class="number">0</span>;</span><br><span class="line"><span class="comment">// num1 num2 num3 num4 =&gt; 1 2 3 4</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> num1 = ~~ -<span class="number">1.19</span>;</span><br><span class="line"><span class="keyword">let</span> num2 = -<span class="number">2.29</span> | <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> num3 = -<span class="number">3.09</span> &gt;&gt; <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> num3 = -<span class="number">4.39</span> ^ <span class="number">0</span>;</span><br><span class="line"><span class="comment">// num1 num2 num3 num4 =&gt; -1 -2 -3 -4</span></span><br></pre></td></tr></table></figure></p><h3 id="零填充"><a href="#零填充" class="headerlink" title="零填充"></a>零填充</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fillZeroStart</span> (<span class="params">num, len</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> num.toString().padStart(len, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// fillZeroStart(12345, 10) =&gt; &#x27;0000012345&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fillZeroEnd</span> (<span class="params">num, len</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> num.toString().padEnd(len, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// fillZeroEnd(12345, 10) =&gt; &#x27;1234500000&#x27;</span></span><br></pre></td></tr></table></figure></p><h3 id="判断奇偶数"><a href="#判断奇偶数" class="headerlink" title="判断奇偶数"></a>判断奇偶数</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 整数部分判断奇偶数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">OddEven</span>(<span class="params">num</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> !!(num &amp; <span class="number">1</span>) ? <span class="string">&#x27;odd&#x27;</span> : <span class="string">&#x27;even&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> num1 = OddEven(<span class="number">11</span>);</span><br><span class="line"><span class="keyword">let</span> num2 = OddEven(-<span class="number">11</span>);</span><br><span class="line"><span class="keyword">let</span> num3 = OddEven(<span class="number">12</span>);</span><br><span class="line"><span class="keyword">let</span> num4 = OddEven(-<span class="number">12.1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// num1 num2 num3 num4 =&gt; &#x27;odd&#x27; &#x27;odd&#x27; &#x27;even&#x27; &#x27;even&#x27;</span></span><br></pre></td></tr></table></figure></p><h3 id="生成范围随机数"><a href="#生成范围随机数" class="headerlink" title="生成范围随机数"></a>生成范围随机数</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">RandomNum</span>(<span class="params">min, max</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * (max - min + <span class="number">1</span>)) + min;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// RandomNum(1, 10) =&gt; 5</span></span><br></pre></td></tr></table></figure></p><h3 id="增加千分位符"><a href="#增加千分位符" class="headerlink" title="增加千分位符"></a>增加千分位符</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">thousandNum</span> (<span class="params">num</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> num.toString().replace(<span class="regexp">/\B(?=(\d&#123;3&#125;)+(?!\d))/g</span>, <span class="string">&quot;,&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// thousandNum(123456.78) =&gt; &#x27;123,456.78&#x27;</span></span><br></pre></td></tr></table></figure></p><h3 id="转化为万、亿、万亿单位"><a href="#转化为万、亿、万亿单位" class="headerlink" title="转化为万、亿、万亿单位"></a>转化为万、亿、万亿单位</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">numberFormat</span> (<span class="params">value</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> k = <span class="number">10000</span>;</span><br><span class="line">  <span class="keyword">let</span> sizes = [<span class="string">&#x27;&#x27;</span>, <span class="string">&#x27;万&#x27;</span>, <span class="string">&#x27;亿&#x27;</span>, <span class="string">&#x27;万亿&#x27;</span>];</span><br><span class="line">  <span class="keyword">if</span> (value &gt;= k) &#123;</span><br><span class="line">    <span class="keyword">let</span> i = <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.log(value) / <span class="built_in">Math</span>.log(k));</span><br><span class="line">    <span class="keyword">return</span> (value / <span class="built_in">Math</span>.pow(k, i)).toFixed(<span class="number">2</span>) + sizes[i];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Number</span>(value).toFixed(<span class="number">2</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// numberFormat(12345678) =&gt; &#x27;1234.57万&#x27;</span></span><br></pre></td></tr></table></figure></p><h2 id="时间"><a href="#时间" class="headerlink" title="时间"></a>时间</h2><h3 id="获取当前日期"><a href="#获取当前日期" class="headerlink" title="获取当前日期"></a>获取当前日期</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="built_in">Date</span>(<span class="keyword">new</span> <span class="built_in">Date</span>().toLocaleDateString());</span><br></pre></td></tr></table></figure></p><h2 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h2><h3 id="截断数组"><a href="#截断数组" class="headerlink" title="截断数组"></a>截断数组</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>];</span><br><span class="line">arr.length = <span class="number">2</span>; <span class="comment">// arr.slice(0, 2);</span></span><br><span class="line"><span class="comment">// arr =&gt; [0, 1]</span></span><br></pre></td></tr></table></figure></p><h3 id="在数组开头插入元素"><a href="#在数组开头插入元素" class="headerlink" title="在数组开头插入元素"></a>在数组开头插入元素</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">1</span>, <span class="number">2</span>];</span><br><span class="line">arr.unshift(<span class="number">0</span>);</span><br><span class="line">arr = [<span class="number">0</span>].concat(arr);</span><br><span class="line">arr = [<span class="number">0</span>, ...arr];</span><br><span class="line"><span class="comment">// arr =&gt; [0, 1, 2]</span></span><br></pre></td></tr></table></figure></p><h3 id="在数组末尾插入元素"><a href="#在数组末尾插入元素" class="headerlink" title="在数组末尾插入元素"></a>在数组末尾插入元素</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">0</span>, <span class="number">1</span>]; </span><br><span class="line">arr.push(<span class="number">2</span>);</span><br><span class="line">arr.concat(<span class="number">2</span>);</span><br><span class="line">arr[arr.length] = <span class="number">2</span>;</span><br><span class="line">arr = [...arr, <span class="number">2</span>];</span><br><span class="line"><span class="comment">// arr =&gt; [0, 1, 2]</span></span><br></pre></td></tr></table></figure></p><h3 id="获取数组中随机元素"><a href="#获取数组中随机元素" class="headerlink" title="获取数组中随机元素"></a>获取数组中随机元素</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line"><span class="keyword">let</span> randomItem = arr[<span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * arr.length)];</span><br><span class="line"><span class="comment">// randomItem =&gt; 4</span></span><br></pre></td></tr></table></figure></p><h3 id="创建指定长度的迭代元素数组​​​​​​​"><a href="#创建指定长度的迭代元素数组​​​​​​​" class="headerlink" title="创建指定长度的迭代元素数组​​​​​​​"></a>创建指定长度的迭代元素数组​​​​​​​</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [...new <span class="built_in">Array</span>(<span class="number">3</span>).keys()];</span><br><span class="line"><span class="comment">// arr =&gt; [0, 1, 2]</span></span><br></pre></td></tr></table></figure></p><h3 id="创建指定长度的相同元素数组​​​​​​​"><a href="#创建指定长度的相同元素数组​​​​​​​" class="headerlink" title="创建指定长度的相同元素数组​​​​​​​"></a>创建指定长度的相同元素数组​​​​​​​</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = <span class="keyword">new</span> <span class="built_in">Array</span>(<span class="number">3</span>).fill(<span class="number">0</span>);</span><br><span class="line"><span class="comment">// arr =&gt; [0, 1, 2]</span></span><br></pre></td></tr></table></figure></p><h3 id="交换数值"><a href="#交换数值" class="headerlink" title="交换数值"></a>交换数值</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> b = <span class="number">1</span>;</span><br><span class="line">[a, b] = [b, a];</span><br><span class="line"><span class="comment">// a b =&gt; 1 0</span></span><br><span class="line"><span class="string">``</span><span class="string">`(</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">### 数组排序</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">`</span><span class="string">``</span>javascript</span><br><span class="line"><span class="keyword">let</span> arr = [<span class="number">4</span>, <span class="number">1</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">5</span>].sort(<span class="function">(<span class="params">a, b</span>) =&gt;</span> a - b) <span class="comment">// 升序</span></span><br><span class="line"><span class="comment">// arr =&gt; [1, 2, 3, 4, 5]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> arr = [<span class="number">4</span>, <span class="number">1</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">5</span>].sort(<span class="function">(<span class="params">a, b</span>) =&gt;</span> b - a); <span class="comment">// 倒序</span></span><br><span class="line"><span class="comment">// arr =&gt; [5, 4, 3, 2, 1]</span></span><br></pre></td></tr></table></figure></p><h3 id="数组去重"><a href="#数组去重" class="headerlink" title="数组去重"></a>数组去重</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [...new <span class="built_in">Set</span>([<span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="literal">null</span>, <span class="literal">null</span>])];</span><br><span class="line"><span class="comment">// arr =&gt; [0, 1, null]</span></span><br></pre></td></tr></table></figure></p><h3 id="数组混淆"><a href="#数组混淆" class="headerlink" title="数组混淆"></a>数组混淆</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>].slice().sort(<span class="function">() =&gt;</span> <span class="built_in">Math</span>.random() - <span class="number">.5</span>);</span><br><span class="line"><span class="comment">// arr =&gt; [3, 4, 0, 5, 1, 2]</span></span><br></pre></td></tr></table></figure></p><h3 id="计算数组元素出现的次数​​​​​​​"><a href="#计算数组元素出现的次数​​​​​​​" class="headerlink" title="计算数组元素出现的次数​​​​​​​"></a>计算数组元素出现的次数​​​​​​​</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">2</span>];</span><br><span class="line"><span class="keyword">let</span> count = arr.reduce(<span class="function">(<span class="params">t, v</span>) =&gt;</span> &#123;</span><br><span class="line">  t[v] = t[v] ? ++t[v] : <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">return</span> t;</span><br><span class="line">&#125;, &#123;&#125;);</span><br><span class="line"><span class="comment">// count =&gt; &#123; 0: 1, 1: 2, 2: 3 &#125;</span></span><br></pre></td></tr></table></figure></p><h3 id="求数组平均值"><a href="#求数组平均值" class="headerlink" title="求数组平均值"></a>求数组平均值</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line"><span class="keyword">let</span> avg = arr.reduce(<span class="function">(<span class="params">a, b</span>) =&gt;</span> a + b) / arr.length</span><br><span class="line"><span class="comment">// avg =&gt; 3</span></span><br></pre></td></tr></table></figure></p><h2 id="颜色"><a href="#颜色" class="headerlink" title="颜色"></a>颜色</h2><h3 id="生成随机-HEX-颜色值"><a href="#生成随机-HEX-颜色值" class="headerlink" title="生成随机 HEX 颜色值"></a>生成随机 HEX 颜色值</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&#x27;#&#x27;</span> + <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">0xffffff</span>).toString(<span class="number">16</span>).padEnd(<span class="number">6</span>, <span class="string">&quot;0&quot;</span>);</span><br><span class="line"><span class="comment">// =&gt; &#x27;#d6259c&#x27;</span></span><br></pre></td></tr></table></figure></p><h3 id="RGB-转-HEX-颜色"><a href="#RGB-转-HEX-颜色" class="headerlink" title="RGB 转 HEX 颜色"></a>RGB 转 HEX 颜色</h3><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">rgbToHex</span> (<span class="params">r, g, b</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">&#x27;#&#x27;</span> + ((<span class="number">1</span> &lt;&lt; <span class="number">24</span>) + (r &lt;&lt; <span class="number">16</span>) + (g &lt;&lt; <span class="number">8</span>) + b).toString(<span class="number">16</span>).slice(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line">rgbToHex(<span class="number">52</span>, <span class="number">45</span>, <span class="number">125</span>);</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/JavaScript/">JavaScript</category>
      
      
      <comments>https://blog.eurkon.com/post/ea69450.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 地图上显示折线图</title>
      <link>https://blog.eurkon.com/post/e83030eb.html</link>
      <guid>https://blog.eurkon.com/post/e83030eb.html</guid>
      <pubDate>Sat, 20 Nov 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var geoCoordMap = {  '河北': [114.4995, 38.1006],//河北 石家庄  '山西': [112.3352, 37.9413],// 山西 太原  '内蒙古': [111.4124, 40.4901],// 内蒙古 呼和浩特  '辽宁': [123.1238, 42.1216],// 辽宁 沈阳  '吉林': [125.8154, 44.2584],// 吉林 长春  '黑龙江': [127.9688, 45.368],// 黑龙江 哈尔滨  '江苏': [118.8062, 31.9208],//江苏 南京  '浙江': [119.5313, 29.8773],// 浙江 杭州  '安徽': [117.29, 32.0581],// 安徽 合肥  '福建': [119.4543, 25.9222],// 福建 福州  '山东': [117.1582, 36.8701],// 山东 济南  '江西': [116.0046, 28.6633],// 江西 南昌  '河南': [113.4668, 34.6234],//河南 郑州  '新疆': [87.9236, 43.5883],// 新疆 乌鲁木齐  '湖北': [114.3896, 30.6628],// 湖北 武汉  '湖南': [113.0823, 28.2568],// 湖南 长沙  '广东': [113.5107, 23.2196],// 广东 广州  '广西': [108.479, 23.1152],// 广西 南宁  '海南': [110.3893, 19.8516],// 海南 海口  '四川': [103.9526, 30.7617],// 四川 成都  '贵州': [106.6992, 26.7682],// 贵州 贵阳  '云南': [102.9199, 25.4663],// 云南 昆明  '西藏': [91.1865, 30.1465],// 西藏 拉萨  '陕西': [109.1162, 34.2004],// 陕西 西安  '青海': [101.4038, 36.8207],// 青海 西宁  '甘肃': [103.5901, 36.3043],// 甘肃 兰州  '宁夏': [106.3586, 38.1775],// 宁夏 银川  '上海': [121.4648, 31.2891],  '东莞': [113.8953, 22.901],  '东营': [118.7073, 37.5513],  '中山': [113.4229, 22.478],  '临汾': [111.4783, 36.1615],  '临沂': [118.3118, 35.2936],  '丹东': [124.541, 40.4242],  '丽水': [119.5642, 28.1854],  '乌鲁木齐': [87.9236, 43.5883],  '佛山': [112.8955, 23.1097],  '保定': [115.0488, 39.0948],  '兰州': [103.5901, 36.3043],  '包头': [110.3467, 41.4899],  '北京': [116.4551, 40.2539],  '北海': [109.314, 21.6211],  '南京': [118.8062, 31.9208],  '南宁': [108.479, 23.1152],  '南昌': [116.0046, 28.6633],  '南通': [121.1023, 32.1625],  '厦门': [118.1689, 24.6478],  '台州': [121.1353, 28.6688],  '合肥': [117.29, 32.0581],  '呼和浩特': [111.4124, 40.4901],  '咸阳': [108.4131, 34.8706],  '哈尔滨': [127.9688, 45.368],  '唐山': [118.4766, 39.6826],  '嘉兴': [120.9155, 30.6354],  '大同': [113.7854, 39.8035],  '大连': [122.2229, 39.4409],  '天津': [117.4219, 39.4189],  '太原': [112.3352, 37.9413],  '威海': [121.9482, 37.1393],  '宁波': [121.5967, 29.6466],  '宝鸡': [107.1826, 34.3433],  '宿迁': [118.5535, 33.7775],  '常州': [119.4543, 31.5582],  '广州': [113.5107, 23.2196],  '廊坊': [116.521, 39.0509],  '延安': [109.1052, 36.4252],  '张家口': [115.1477, 40.8527],  '徐州': [117.5208, 34.3268],  '德州': [116.6858, 37.2107],  '惠州': [114.6204, 23.1647],  '成都': [103.9526, 30.7617],  '扬州': [119.4653, 32.8162],  '承德': [117.5757, 41.4075],  '拉萨': [91.1865, 30.1465],  '无锡': [120.3442, 31.5527],  '日照': [119.2786, 35.5023],  '昆明': [102.9199, 25.4663],  '杭州': [119.5313, 29.8773],  '枣庄': [117.323, 34.8926],  '柳州': [109.3799, 24.9774],  '株洲': [113.5327, 27.0319],  '武汉': [114.3896, 30.6628],  '汕头': [117.1692, 23.3405],  '江门': [112.6318, 22.1484],  '沈阳': [123.1238, 42.1216],  '沧州': [116.8286, 38.2104],  '河源': [114.917, 23.9722],  '泉州': [118.3228, 25.1147],  '泰安': [117.0264, 36.0516],  '泰州': [120.0586, 32.5525],  '济南': [117.1582, 36.8701],  '济宁': [116.8286, 35.3375],  '海口': [110.3893, 19.8516],  '淄博': [118.0371, 36.6064],  '淮安': [118.927, 33.4039],  '深圳': [114.5435, 22.5439],  '清远': [112.9175, 24.3292],  '温州': [120.498, 27.8119],  '渭南': [109.7864, 35.0299],  '湖州': [119.8608, 30.7782],  '湘潭': [112.5439, 27.7075],  '滨州': [117.8174, 37.4963],  '潍坊': [119.0918, 36.524],  '烟台': [120.7397, 37.5128],  '玉溪': [101.9312, 23.8898],  '珠海': [113.7305, 22.1155],  '盐城': [120.2234, 33.5577],  '盘锦': [121.9482, 41.0449],  '石家庄': [114.4995, 38.1006],  '福州': [119.4543, 25.9222],  '秦皇岛': [119.2126, 40.0232],  '绍兴': [120.564, 29.7565],  '聊城': [115.9167, 36.4032],  '肇庆': [112.1265, 23.5822],  '舟山': [122.2559, 30.2234],  '苏州': [120.6519, 31.3989],  '莱芜': [117.6526, 36.2714],  '菏泽': [115.6201, 35.2057],  '营口': [122.4316, 40.4297],  '葫芦岛': [120.1575, 40.578],  '衡水': [115.8838, 37.7161],  '衢州': [118.6853, 28.8666],  '西宁': [101.4038, 36.8207],  '西安': [109.1162, 34.2004],  '贵阳': [106.6992, 26.7682],  '连云港': [119.1248, 34.552],  '邢台': [114.8071, 37.2821],  '邯郸': [114.4775, 36.535],  '郑州': [113.4668, 34.6234],  '鄂尔多斯': [108.9734, 39.2487],  '重庆': [107.7539, 30.1904],  '金华': [120.0037, 29.1028],  '铜川': [109.0393, 35.1947],  '银川': [106.3586, 38.1775],  '镇江': [119.4763, 31.9702],  '长春': [125.8154, 44.2584],  '长沙': [113.0823, 28.2568],  '长治': [112.8625, 36.4746],  '阳泉': [113.4778, 38.0951],  '青岛': [120.4651, 36.3373],  '韶关': [113.7964, 24.7028]};var data = [  {    'name': '城市',    'data': [      '吉林',      '吉林',      '吉林',      '吉林',      '四川',      '四川',      '四川',      '四川',      '山东',      '山东',      '山东',      '山东',      '陕西',      '陕西',      '陕西',      '陕西',      '上海',      '上海',      '上海',      '上海',      '大连',      '大连',      '大连',      '大连',      '青海',      '青海',      '青海',      '青海',      '西藏',      '西藏',      '西藏',      '西藏',      '广东',      '广东',      '广东',      '广东',      '海南',      '海南',      '海南',      '海南',      '沈阳',      '沈阳',      '沈阳',      '沈阳',      '云南',      '云南',      '云南',      '云南',      '湖南',      '湖南',      '湖南',      '湖南',      '新疆',      '新疆',      '新疆',      '新疆',      '内蒙古',      '内蒙古',      '内蒙古',      '内蒙古',      '宁夏',      '宁夏',      '宁夏',      '宁夏',      '黑龙江',      '黑龙江',      '黑龙江',      '黑龙江',      '河南',      '河南',      '河南',      '河南',      '福建',      '福建',      '福建',      '福建'    ]  },  {    'name': '日期',    'data': [      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',    ]  },  {    'name': '促销数量',    'data': [      459,      444,      852,      494,      481,      492,      491,      440,      496,      466,      856,      480,      432,      842,      834,      474,      455,      464,      445,      490,      478,      468,      485,      860,      463,      487,      443,      488,      836,      453,      447,      498,      858,      493,      840,      469,      486,      489,      458,      454,      450,      439,      434,      846,      854,      838,      484,      844,      473,      470,      452,      441,      477,      438,      442,      497,      500,      436,      850,      460,      449,      465,      462,      461,      457,      448,      435,      433,      479,      431,      482,      467,      437,      475,      848,      446    ]  },  {    'name': '挑拨数量',    'data': [      200,      172,      300,      200,      200,      200,      200,      164,      200,      200,      300,      200,      150,      300,      300,      200,      194,      200,      174,      200,      200,      200,      200,      300,      200,      200,      170,      200,      300,      190,      178,      200,      300,      200,      300,      200,      200,      200,      200,      192,      184,      162,      152,      300,      300,      300,      200,      300,      200,      200,      188,      166,      200,      160,      168,      200,      200,      156,      300,      200,      182,      200,      200,      200,      198,      180,      154,      150,      200,      150,      200,      200,      158,      200,      300,      176    ]  },  {    'name': '退回数量',    'data': [      150,      150,      150,      140,      135,      150,      150,      150,      143,      128,      220,      150,      150,      150,      150,      124,      150,      150,      224,      150,      116,      210,      202,      150,      139,      150,      129,      150,      150,      150,      131,      150,      226,      150,      208,      150,      150,      150,      142,      138,      134,      123,      118,      214,      222,      206,      150,      212,      150,      150,      136,      125,      150,      122,      126,      150,      150,      120,      218,      144,      133,      150,      150,      145,      141,      132,      119,      117,      150,      115,      150,      150,      121,      150,      216,      130    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  dim: 0, // 维度（一个）  compare: 1, // 对比（一个）（饼状图没有此属性）  values: [2, 3, 4], // 数值（多个）  type: 'line', // 图表类型，柱状图，折线图，饼图  stack: false // 是否显示堆积图（堆积柱状图或堆积折线图，饼状图没有此属性）}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar legendArr = []for (let i in config.values) {  legendArr.push(data[config.values[i]].name)}var seriesList = []if (config.compare !== '') {  let seriesData = {}  for (let j = 0; j < rowNum; j++) {    let dim = data[config.dim].data[j]    if (dim in seriesData) {      config.values.forEach((item, index) => {        seriesData[dim][index].data.push({ 'name': data[config.compare].data[j], 'value': data[item].data[j] })      })    }    else {      seriesData[dim] = config.values.map((item, index) => {        return { 'name': data[item].name, 'data': [{ 'name': data[config.compare].data[j], 'value': data[item].data[j] }] }      })    }  }  Object.keys(seriesData).forEach(item => seriesList.push({ 'name': item, 'data': seriesData[item] }))}else {  for (let j = 0; j < rowNum; j++) {    seriesList.push({ 'name': data[0].data[j], 'value': config.values.map(item => { return data[item].data[j] }) })  }}var option = {  top: '-40%',  bottom: '-40%',  geo: {    type: 'map',    map: 'china',    label: {      normal: { show: false },      emphasis: { show: false, color: '#617282' }    },    itemStyle: {      normal: {        areaColor: 'rgb(230, 232, 234)',        borderColor: 'rgb(255, 255, 255)',        borderWidth: 1      },      emphasis: {        areaColor: 'rgb(186, 218, 85)'      }    }  },  series: []};function renderEachCity () {  let options = {    color: config.colors,    legend: {      data: legendArr,      textStyle: {        color: config.theme === 'DARK' ? '#B4C2D6' : '#617282'      }    },    tooltip: {      trigger: config.type === 'pie' ? 'item' : 'axis'    },    xAxis: [],    yAxis: [],    grid: [],    series: []  };  echarts.util.each(seriesList, (dataItem, idx) => {    let geoCoord = geoCoordMap[dataItem.name];    let coord = chart.convertToPixel('geo', geoCoord);    idx += '';    if (config.type === 'line' || config.type === 'bar') { renderGrid(options, dataItem, idx, coord) }    if (config.type === 'pie') { renderPie(options, dataItem, idx, coord) }  })  chart.setOption(options);}function renderGrid (options, dataItem, idx, coord) {  let compareList = legendArr  if (config.compare !== '') {    compareList = []    for (let i = 0; i < dataItem.data.length; i++) {      for (let j = 0; j < dataItem.data[i].data.length; j++) {        compareList.push(dataItem.data[i].data[j].name)      }    }  }  options.xAxis.push({    id: idx,    gridId: idx,    type: 'category',    name: dataItem.name,    nameLocation: 'middle',    nameGap: 3,    splitLine: { show: false },    axisTick: { show: false },    axisLabel: { show: false },    axisLine: {      show: true,      lineStyle: {        color: '#617282'      },    },    nameTextStyle: { color: '#617282' },    data: config.compare !== '' ? Array.from(new Set(compareList)) : [dataItem.name]  });  options.yAxis.push({    id: idx,    gridId: idx,    splitLine: { show: false },    axisTick: { show: false },    axisLabel: { show: false, },    axisLine: {      show: true,      onZero: false,      lineStyle: {        color: '#617282'      },    },    nameTextStyle: { color: '#617282' },  });  options.grid.push({    id: idx,    width: 40,    height: 30,    left: coord[0] - 15,    top: coord[1] - 25  });  if (config.compare !== '') {    for (let i = 0; i < dataItem.data.length; i++) {      options.series.push({        name: dataItem.data[i].name,        type: config.type,        stack: config.stack === true ? idx : false,        areaStyle: { opacity: config.stack === true ? 1 : 0 },        xAxisId: idx,        yAxisId: idx,        data: dataItem.data[i].data      });    }  }  else {    for (let i = 0; i < legendArr.length; i++) {      options.series.push({        name: legendArr[i],        type: config.type,        stack: config.stack === true ? idx : false,        areaStyle: { opacity: config.stack === true ? 1 : 0 },        xAxisId: idx,        yAxisId: idx,        data: [dataItem.value[i]]      });    }  }}function renderPie (options, dataItem, idx, coord) {  options.series.push({    name: dataItem.name,    type: 'pie',    width: 50,    height: 80,    left: coord[0] - 30,    top: coord[1] - 40,    radius: ['40%', '70%'],    itemStyle: {      borderRadius: 10,      borderColor: '#fff',      borderWidth: 2    },    label: {      position: 'center',      formatter: dataItem.name,      color: '#617282'    },    emphasis: {      label: {        show: false      }    },    data: dataItem.value.map((item, index) => {      return { 'name': legendArr[index], 'value': item }    })  })}chart.setOption(option);renderEachCity();window.addEventListener('resize', () => {  chart.resize();  renderEachCity();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/e83030eb.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 地图上显示柱状图</title>
      <link>https://blog.eurkon.com/post/5a6784e7.html</link>
      <guid>https://blog.eurkon.com/post/5a6784e7.html</guid>
      <pubDate>Wed, 10 Nov 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var geoCoordMap = {  '河北': [114.4995, 38.1006],//河北 石家庄  '山西': [112.3352, 37.9413],// 山西 太原  '内蒙古': [111.4124, 40.4901],// 内蒙古 呼和浩特  '辽宁': [123.1238, 42.1216],// 辽宁 沈阳  '吉林': [125.8154, 44.2584],// 吉林 长春  '黑龙江': [127.9688, 45.368],// 黑龙江 哈尔滨  '江苏': [118.8062, 31.9208],//江苏 南京  '浙江': [119.5313, 29.8773],// 浙江 杭州  '安徽': [117.29, 32.0581],// 安徽 合肥  '福建': [119.4543, 25.9222],// 福建 福州  '山东': [117.1582, 36.8701],// 山东 济南  '江西': [116.0046, 28.6633],// 江西 南昌  '河南': [113.4668, 34.6234],//河南 郑州  '新疆': [87.9236, 43.5883],// 新疆 乌鲁木齐  '湖北': [114.3896, 30.6628],// 湖北 武汉  '湖南': [113.0823, 28.2568],// 湖南 长沙  '广东': [113.5107, 23.2196],// 广东 广州  '广西': [108.479, 23.1152],// 广西 南宁  '海南': [110.3893, 19.8516],// 海南 海口  '四川': [103.9526, 30.7617],// 四川 成都  '贵州': [106.6992, 26.7682],// 贵州 贵阳  '云南': [102.9199, 25.4663],// 云南 昆明  '西藏': [91.1865, 30.1465],// 西藏 拉萨  '陕西': [109.1162, 34.2004],// 陕西 西安  '青海': [101.4038, 36.8207],// 青海 西宁  '甘肃': [103.5901, 36.3043],// 甘肃 兰州  '宁夏': [106.3586, 38.1775],// 宁夏 银川  '上海': [121.4648, 31.2891],  '东莞': [113.8953, 22.901],  '东营': [118.7073, 37.5513],  '中山': [113.4229, 22.478],  '临汾': [111.4783, 36.1615],  '临沂': [118.3118, 35.2936],  '丹东': [124.541, 40.4242],  '丽水': [119.5642, 28.1854],  '乌鲁木齐': [87.9236, 43.5883],  '佛山': [112.8955, 23.1097],  '保定': [115.0488, 39.0948],  '兰州': [103.5901, 36.3043],  '包头': [110.3467, 41.4899],  '北京': [116.4551, 40.2539],  '北海': [109.314, 21.6211],  '南京': [118.8062, 31.9208],  '南宁': [108.479, 23.1152],  '南昌': [116.0046, 28.6633],  '南通': [121.1023, 32.1625],  '厦门': [118.1689, 24.6478],  '台州': [121.1353, 28.6688],  '合肥': [117.29, 32.0581],  '呼和浩特': [111.4124, 40.4901],  '咸阳': [108.4131, 34.8706],  '哈尔滨': [127.9688, 45.368],  '唐山': [118.4766, 39.6826],  '嘉兴': [120.9155, 30.6354],  '大同': [113.7854, 39.8035],  '大连': [122.2229, 39.4409],  '天津': [117.4219, 39.4189],  '太原': [112.3352, 37.9413],  '威海': [121.9482, 37.1393],  '宁波': [121.5967, 29.6466],  '宝鸡': [107.1826, 34.3433],  '宿迁': [118.5535, 33.7775],  '常州': [119.4543, 31.5582],  '广州': [113.5107, 23.2196],  '廊坊': [116.521, 39.0509],  '延安': [109.1052, 36.4252],  '张家口': [115.1477, 40.8527],  '徐州': [117.5208, 34.3268],  '德州': [116.6858, 37.2107],  '惠州': [114.6204, 23.1647],  '成都': [103.9526, 30.7617],  '扬州': [119.4653, 32.8162],  '承德': [117.5757, 41.4075],  '拉萨': [91.1865, 30.1465],  '无锡': [120.3442, 31.5527],  '日照': [119.2786, 35.5023],  '昆明': [102.9199, 25.4663],  '杭州': [119.5313, 29.8773],  '枣庄': [117.323, 34.8926],  '柳州': [109.3799, 24.9774],  '株洲': [113.5327, 27.0319],  '武汉': [114.3896, 30.6628],  '汕头': [117.1692, 23.3405],  '江门': [112.6318, 22.1484],  '沈阳': [123.1238, 42.1216],  '沧州': [116.8286, 38.2104],  '河源': [114.917, 23.9722],  '泉州': [118.3228, 25.1147],  '泰安': [117.0264, 36.0516],  '泰州': [120.0586, 32.5525],  '济南': [117.1582, 36.8701],  '济宁': [116.8286, 35.3375],  '海口': [110.3893, 19.8516],  '淄博': [118.0371, 36.6064],  '淮安': [118.927, 33.4039],  '深圳': [114.5435, 22.5439],  '清远': [112.9175, 24.3292],  '温州': [120.498, 27.8119],  '渭南': [109.7864, 35.0299],  '湖州': [119.8608, 30.7782],  '湘潭': [112.5439, 27.7075],  '滨州': [117.8174, 37.4963],  '潍坊': [119.0918, 36.524],  '烟台': [120.7397, 37.5128],  '玉溪': [101.9312, 23.8898],  '珠海': [113.7305, 22.1155],  '盐城': [120.2234, 33.5577],  '盘锦': [121.9482, 41.0449],  '石家庄': [114.4995, 38.1006],  '福州': [119.4543, 25.9222],  '秦皇岛': [119.2126, 40.0232],  '绍兴': [120.564, 29.7565],  '聊城': [115.9167, 36.4032],  '肇庆': [112.1265, 23.5822],  '舟山': [122.2559, 30.2234],  '苏州': [120.6519, 31.3989],  '莱芜': [117.6526, 36.2714],  '菏泽': [115.6201, 35.2057],  '营口': [122.4316, 40.4297],  '葫芦岛': [120.1575, 40.578],  '衡水': [115.8838, 37.7161],  '衢州': [118.6853, 28.8666],  '西宁': [101.4038, 36.8207],  '西安': [109.1162, 34.2004],  '贵阳': [106.6992, 26.7682],  '连云港': [119.1248, 34.552],  '邢台': [114.8071, 37.2821],  '邯郸': [114.4775, 36.535],  '郑州': [113.4668, 34.6234],  '鄂尔多斯': [108.9734, 39.2487],  '重庆': [107.7539, 30.1904],  '金华': [120.0037, 29.1028],  '铜川': [109.0393, 35.1947],  '银川': [106.3586, 38.1775],  '镇江': [119.4763, 31.9702],  '长春': [125.8154, 44.2584],  '长沙': [113.0823, 28.2568],  '长治': [112.8625, 36.4746],  '阳泉': [113.4778, 38.0951],  '青岛': [120.4651, 36.3373],  '韶关': [113.7964, 24.7028]};var data = [  {    'name': '城市',    'data': [      '四川',      '吉林',      '湖南',      '大连',      '新疆',      '山东',      '海南',      '山西',      '新疆',      '陕西',      '大连',      '新疆',      '青海',      '四川',      '陕西',      '山西',      '吉林',      '内蒙古',      '陕西',      '浙江',      '山东',      '湖南',      '四川',      '广东',      '山西',      '广东',      '内蒙古',      '海南',      '广西',      '浙江',      '内蒙古',      '青海',      '广西',      '山西',      '大连',      '广东',      '广西',      '内蒙古',      '新疆'    ]  },  {    'name': '商品小类',    'data': [      '休闲零食',      '护肤品',      '休闲零食',      '软饮',      '工具',      '休闲零食',      '休闲零食',      '休闲零食',      '软饮',      '休闲零食',      '工具',      '护肤品',      '护肤品',      '软饮',      '工具',      '护肤品',      '休闲零食',      '软饮',      '软饮',      '休闲零食',      '护肤品',      '软饮',      '护肤品',      '休闲零食',      '软饮',      '软饮',      '护肤品',      '软饮',      '工具',      '工具',      '工具',      '休闲零食',      '休闲零食',      '工具',      '休闲零食',      '护肤品',      '软饮',      '休闲零食',      '休闲零食'    ]  },  {    'name': '促销数量',    'data': [      1779,      451,      450,      433,      441,      2272,      1307,      3151,      1788,      921,      452,      484,      473,      497,      959,      495,      1391,      1377,      3069,      1404,      462,      1388,      440,      1387,      464,      475,      858,      1421,      485,      860,      474,      1381,      856,      838,      961,      836,      913,      2242,      4108    ]  },  {    'name': '挑拨数量',    'data': [      700,      186,      184,      150,      166,      870,      500,      1232,      690,      362,      188,      200,      200,      200,      400,      200,      550,      550,      1172,      582,      200,      550,      164,      554,      200,      200,      300,      568,      200,      300,      200,      546,      300,      300,      400,      300,      378,      880,      1628    ]  },  {    'name': '退回数量',    'data': [      503,      135,      134,      117,      125,      643,      359,      912,      499,      273,      136,      150,      150,      150,      300,      150,      416,      409,      846,      433,      150,      415,      124,      411,      150,      150,      226,      426,      150,      228,      150,      407,      224,      206,      300,      204,      281,      634,      1214    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  dim: 0, // 维度（一个）  compare: 1, // 对比（一个）（饼状图没有此属性）  values: [2, 3, 4], // 数值（多个）  type: 'bar', // 图表类型，柱状图，折线图，饼图  stack: false // 是否显示堆积图（堆积柱状图或堆积折线图，饼状图没有此属性）}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar legendArr = []for (let i in config.values) {  legendArr.push(data[config.values[i]].name)}var seriesList = []if (config.compare !== '') {  let seriesData = {}  for (let j = 0; j < rowNum; j++) {    let dim = data[config.dim].data[j]    if (dim in seriesData) {      config.values.forEach((item, index) => {        seriesData[dim][index].data.push({ 'name': data[config.compare].data[j], 'value': data[item].data[j] })      })    }    else {      seriesData[dim] = config.values.map((item, index) => {        return { 'name': data[item].name, 'data': [{ 'name': data[config.compare].data[j], 'value': data[item].data[j] }] }      })    }  }  Object.keys(seriesData).forEach(item => seriesList.push({ 'name': item, 'data': seriesData[item] }))}else {  for (let j = 0; j < rowNum; j++) {    seriesList.push({ 'name': data[0].data[j], 'value': config.values.map(item => { return data[item].data[j] }) })  }}var option = {  top: '-40%',  bottom: '-40%',  geo: {    type: 'map',    map: 'china',    label: {      normal: { show: false },      emphasis: { show: false, color: '#617282' }    },    itemStyle: {      normal: {        areaColor: 'rgb(230, 232, 234)',        borderColor: 'rgb(255, 255, 255)',        borderWidth: 1      },      emphasis: {        areaColor: 'rgb(186, 218, 85)'      }    }  },  series: []};function renderEachCity () {  let options = {    color: config.colors,    legend: {      data: legendArr,      textStyle: {        color: config.theme === 'DARK' ? '#B4C2D6' : '#617282'      }    },    tooltip: {      trigger: config.type === 'pie' ? 'item' : 'axis'    },    xAxis: [],    yAxis: [],    grid: [],    series: []  };  echarts.util.each(seriesList, (dataItem, idx) => {    let geoCoord = geoCoordMap[dataItem.name];    let coord = chart.convertToPixel('geo', geoCoord);    idx += '';    if (config.type === 'line' || config.type === 'bar') { renderGrid(options, dataItem, idx, coord) }    if (config.type === 'pie') { renderPie(options, dataItem, idx, coord) }  })  chart.setOption(options);}function renderGrid (options, dataItem, idx, coord) {  let compareList = legendArr  if (config.compare !== '') {    compareList = []    for (let i = 0; i < dataItem.data.length; i++) {      for (let j = 0; j < dataItem.data[i].data.length; j++) {        compareList.push(dataItem.data[i].data[j].name)      }    }  }  options.xAxis.push({    id: idx,    gridId: idx,    type: 'category',    name: dataItem.name,    nameLocation: 'middle',    nameGap: 3,    splitLine: { show: false },    axisTick: { show: false },    axisLabel: { show: false },    axisLine: {      show: true,      lineStyle: {        color: '#617282'      },    },    nameTextStyle: { color: '#617282' },    data: config.compare !== '' ? Array.from(new Set(compareList)) : [dataItem.name]  });  options.yAxis.push({    id: idx,    gridId: idx,    splitLine: { show: false },    axisTick: { show: false },    axisLabel: { show: false, },    axisLine: {      show: true,      onZero: false,      lineStyle: {        color: '#617282'      },    },    nameTextStyle: { color: '#617282' },  });  options.grid.push({    id: idx,    width: 40,    height: 30,    left: coord[0] - 15,    top: coord[1] - 25  });  if (config.compare !== '') {    for (let i = 0; i < dataItem.data.length; i++) {      options.series.push({        name: dataItem.data[i].name,        type: config.type,        stack: config.stack === true ? idx : false,        areaStyle: { opacity: config.stack === true ? 1 : 0 },        xAxisId: idx,        yAxisId: idx,        data: dataItem.data[i].data      });    }  }  else {    for (let i = 0; i < legendArr.length; i++) {      options.series.push({        name: legendArr[i],        type: config.type,        stack: config.stack === true ? idx : false,        areaStyle: { opacity: config.stack === true ? 1 : 0 },        xAxisId: idx,        yAxisId: idx,        data: [dataItem.value[i]]      });    }  }}function renderPie (options, dataItem, idx, coord) {  options.series.push({    name: dataItem.name,    type: 'pie',    width: 50,    height: 80,    left: coord[0] - 30,    top: coord[1] - 40,    radius: ['40%', '70%'],    itemStyle: {      borderRadius: 10,      borderColor: '#fff',      borderWidth: 2    },    label: {      position: 'center',      formatter: dataItem.name,      color: '#617282'    },    emphasis: {      label: {        show: false      }    },    data: dataItem.value.map((item, index) => {      return { 'name': legendArr[index], 'value': item }    })  })}chart.setOption(option);renderEachCity();window.addEventListener('resize', () => {  chart.resize();  renderEachCity();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/5a6784e7.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 地图上显示饼图</title>
      <link>https://blog.eurkon.com/post/d8b77f28.html</link>
      <guid>https://blog.eurkon.com/post/d8b77f28.html</guid>
      <pubDate>Mon, 01 Nov 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var geoCoordMap = {  '河北': [114.4995, 38.1006],//河北 石家庄  '山西': [112.3352, 37.9413],// 山西 太原  '内蒙古': [111.4124, 40.4901],// 内蒙古 呼和浩特  '辽宁': [123.1238, 42.1216],// 辽宁 沈阳  '吉林': [125.8154, 44.2584],// 吉林 长春  '黑龙江': [127.9688, 45.368],// 黑龙江 哈尔滨  '江苏': [118.8062, 31.9208],//江苏 南京  '浙江': [119.5313, 29.8773],// 浙江 杭州  '安徽': [117.29, 32.0581],// 安徽 合肥  '福建': [119.4543, 25.9222],// 福建 福州  '山东': [117.1582, 36.8701],// 山东 济南  '江西': [116.0046, 28.6633],// 江西 南昌  '河南': [113.4668, 34.6234],//河南 郑州  '新疆': [87.9236, 43.5883],// 新疆 乌鲁木齐  '湖北': [114.3896, 30.6628],// 湖北 武汉  '湖南': [113.0823, 28.2568],// 湖南 长沙  '广东': [113.5107, 23.2196],// 广东 广州  '广西': [108.479, 23.1152],// 广西 南宁  '海南': [110.3893, 19.8516],// 海南 海口  '四川': [103.9526, 30.7617],// 四川 成都  '贵州': [106.6992, 26.7682],// 贵州 贵阳  '云南': [102.9199, 25.4663],// 云南 昆明  '西藏': [91.1865, 30.1465],// 西藏 拉萨  '陕西': [109.1162, 34.2004],// 陕西 西安  '青海': [101.4038, 36.8207],// 青海 西宁  '甘肃': [103.5901, 36.3043],// 甘肃 兰州  '宁夏': [106.3586, 38.1775],// 宁夏 银川  '上海': [121.4648, 31.2891],  '东莞': [113.8953, 22.901],  '东营': [118.7073, 37.5513],  '中山': [113.4229, 22.478],  '临汾': [111.4783, 36.1615],  '临沂': [118.3118, 35.2936],  '丹东': [124.541, 40.4242],  '丽水': [119.5642, 28.1854],  '乌鲁木齐': [87.9236, 43.5883],  '佛山': [112.8955, 23.1097],  '保定': [115.0488, 39.0948],  '兰州': [103.5901, 36.3043],  '包头': [110.3467, 41.4899],  '北京': [116.4551, 40.2539],  '北海': [109.314, 21.6211],  '南京': [118.8062, 31.9208],  '南宁': [108.479, 23.1152],  '南昌': [116.0046, 28.6633],  '南通': [121.1023, 32.1625],  '厦门': [118.1689, 24.6478],  '台州': [121.1353, 28.6688],  '合肥': [117.29, 32.0581],  '呼和浩特': [111.4124, 40.4901],  '咸阳': [108.4131, 34.8706],  '哈尔滨': [127.9688, 45.368],  '唐山': [118.4766, 39.6826],  '嘉兴': [120.9155, 30.6354],  '大同': [113.7854, 39.8035],  '大连': [122.2229, 39.4409],  '天津': [117.4219, 39.4189],  '太原': [112.3352, 37.9413],  '威海': [121.9482, 37.1393],  '宁波': [121.5967, 29.6466],  '宝鸡': [107.1826, 34.3433],  '宿迁': [118.5535, 33.7775],  '常州': [119.4543, 31.5582],  '广州': [113.5107, 23.2196],  '廊坊': [116.521, 39.0509],  '延安': [109.1052, 36.4252],  '张家口': [115.1477, 40.8527],  '徐州': [117.5208, 34.3268],  '德州': [116.6858, 37.2107],  '惠州': [114.6204, 23.1647],  '成都': [103.9526, 30.7617],  '扬州': [119.4653, 32.8162],  '承德': [117.5757, 41.4075],  '拉萨': [91.1865, 30.1465],  '无锡': [120.3442, 31.5527],  '日照': [119.2786, 35.5023],  '昆明': [102.9199, 25.4663],  '杭州': [119.5313, 29.8773],  '枣庄': [117.323, 34.8926],  '柳州': [109.3799, 24.9774],  '株洲': [113.5327, 27.0319],  '武汉': [114.3896, 30.6628],  '汕头': [117.1692, 23.3405],  '江门': [112.6318, 22.1484],  '沈阳': [123.1238, 42.1216],  '沧州': [116.8286, 38.2104],  '河源': [114.917, 23.9722],  '泉州': [118.3228, 25.1147],  '泰安': [117.0264, 36.0516],  '泰州': [120.0586, 32.5525],  '济南': [117.1582, 36.8701],  '济宁': [116.8286, 35.3375],  '海口': [110.3893, 19.8516],  '淄博': [118.0371, 36.6064],  '淮安': [118.927, 33.4039],  '深圳': [114.5435, 22.5439],  '清远': [112.9175, 24.3292],  '温州': [120.498, 27.8119],  '渭南': [109.7864, 35.0299],  '湖州': [119.8608, 30.7782],  '湘潭': [112.5439, 27.7075],  '滨州': [117.8174, 37.4963],  '潍坊': [119.0918, 36.524],  '烟台': [120.7397, 37.5128],  '玉溪': [101.9312, 23.8898],  '珠海': [113.7305, 22.1155],  '盐城': [120.2234, 33.5577],  '盘锦': [121.9482, 41.0449],  '石家庄': [114.4995, 38.1006],  '福州': [119.4543, 25.9222],  '秦皇岛': [119.2126, 40.0232],  '绍兴': [120.564, 29.7565],  '聊城': [115.9167, 36.4032],  '肇庆': [112.1265, 23.5822],  '舟山': [122.2559, 30.2234],  '苏州': [120.6519, 31.3989],  '莱芜': [117.6526, 36.2714],  '菏泽': [115.6201, 35.2057],  '营口': [122.4316, 40.4297],  '葫芦岛': [120.1575, 40.578],  '衡水': [115.8838, 37.7161],  '衢州': [118.6853, 28.8666],  '西宁': [101.4038, 36.8207],  '西安': [109.1162, 34.2004],  '贵阳': [106.6992, 26.7682],  '连云港': [119.1248, 34.552],  '邢台': [114.8071, 37.2821],  '邯郸': [114.4775, 36.535],  '郑州': [113.4668, 34.6234],  '鄂尔多斯': [108.9734, 39.2487],  '重庆': [107.7539, 30.1904],  '金华': [120.0037, 29.1028],  '铜川': [109.0393, 35.1947],  '银川': [106.3586, 38.1775],  '镇江': [119.4763, 31.9702],  '长春': [125.8154, 44.2584],  '长沙': [113.0823, 28.2568],  '长治': [112.8625, 36.4746],  '阳泉': [113.4778, 38.0951],  '青岛': [120.4651, 36.3373],  '韶关': [113.7964, 24.7028]};var data = [  {    'name': '城市',    'data': [      '山西',      '广东',      '浙江',      '山东',      '上海',      '陕西',      '海南',      '沈阳',      '河北',      '江苏',      '大连',      '吉林',      '广西',      '四川'    ]  },  {    'name': '促销数量',    'data': [      4948,      2698,      2264,      2734,      4951,      4949,      2728,      1854,      6821,      1838,      1846,      1842,      2254,      2716    ]  },  {    'name': '挑拨数量',    'data': [      1932,      1054,      882,      1070,      1930,      1934,      1068,      746,      2684,      734,      738,      736,      878,      1064    ]  },  {    'name': '退回数量',    'data': [      1418,      765,      661,      793,      1419,      1419,      785,      557,      1988,      549,      553,      551,      655,      777    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  dim: 0, // 维度（一个）  compare: '', // 对比（一个）（饼状图没有此属性）  values: [1, 2, 3], // 数值（多个）  type: 'pie', // 图表类型，柱状图，折线图，饼图  stack: false // 是否显示堆积图（堆积柱状图或堆积折线图，饼状图没有此属性）}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar legendArr = []for (let i in config.values) {  legendArr.push(data[config.values[i]].name)}var seriesList = []if (config.compare !== '') {  let seriesData = {}  for (let j = 0; j < rowNum; j++) {    let dim = data[config.dim].data[j]    if (dim in seriesData) {      config.values.forEach((item, index) => {        seriesData[dim][index].data.push({ 'name': data[config.compare].data[j], 'value': data[item].data[j] })      })    }    else {      seriesData[dim] = config.values.map((item, index) => {        return { 'name': data[item].name, 'data': [{ 'name': data[config.compare].data[j], 'value': data[item].data[j] }] }      })    }  }  Object.keys(seriesData).forEach(item => seriesList.push({ 'name': item, 'data': seriesData[item] }))}else {  for (let j = 0; j < rowNum; j++) {    seriesList.push({ 'name': data[0].data[j], 'value': config.values.map(item => { return data[item].data[j] }) })  }}var option = {  top: '-40%',  bottom: '-40%',  geo: {    type: 'map',    map: 'china',    label: {      normal: { show: false },      emphasis: { show: false, color: '#617282' }    },    itemStyle: {      normal: {        areaColor: 'rgb(230, 232, 234)',        borderColor: 'rgb(255, 255, 255)',        borderWidth: 1      },      emphasis: {        areaColor: 'rgb(186, 218, 85)'      }    }  },  series: []};function renderEachCity () {  let options = {    color: config.colors,    legend: {      data: legendArr,      textStyle: {        color: config.theme === 'DARK' ? '#B4C2D6' : '#617282'      }    },    tooltip: {      trigger: config.type === 'pie' ? 'item' : 'axis'    },    xAxis: [],    yAxis: [],    grid: [],    series: []  };  echarts.util.each(seriesList, (dataItem, idx) => {    let geoCoord = geoCoordMap[dataItem.name];    let coord = chart.convertToPixel('geo', geoCoord);    idx += '';    if (config.type === 'line' || config.type === 'bar') { renderGrid(options, dataItem, idx, coord) }    if (config.type === 'pie') { renderPie(options, dataItem, idx, coord) }  })  chart.setOption(options);}function renderGrid (options, dataItem, idx, coord) {  let compareList = legendArr  if (config.compare !== '') {    compareList = []    for (let i = 0; i < dataItem.data.length; i++) {      for (let j = 0; j < dataItem.data[i].data.length; j++) {        compareList.push(dataItem.data[i].data[j].name)      }    }  }  options.xAxis.push({    id: idx,    gridId: idx,    type: 'category',    name: dataItem.name,    nameLocation: 'middle',    nameGap: 3,    splitLine: { show: false },    axisTick: { show: false },    axisLabel: { show: false },    axisLine: {      show: true,      lineStyle: {        color: '#617282'      },    },    nameTextStyle: { color: '#617282' },    data: config.compare !== '' ? Array.from(new Set(compareList)) : [dataItem.name]  });  options.yAxis.push({    id: idx,    gridId: idx,    splitLine: { show: false },    axisTick: { show: false },    axisLabel: { show: false, },    axisLine: {      show: true,      onZero: false,      lineStyle: {        color: '#617282'      },    },    nameTextStyle: { color: '#617282' },  });  options.grid.push({    id: idx,    width: 40,    height: 30,    left: coord[0] - 15,    top: coord[1] - 25  });  if (config.compare !== '') {    for (let i = 0; i < dataItem.data.length; i++) {      options.series.push({        name: dataItem.data[i].name,        type: config.type,        stack: config.stack === true ? idx : false,        areaStyle: { opacity: config.stack === true ? 1 : 0 },        xAxisId: idx,        yAxisId: idx,        data: dataItem.data[i].data      });    }  }  else {    for (let i = 0; i < legendArr.length; i++) {      options.series.push({        name: legendArr[i],        type: config.type,        stack: config.stack === true ? idx : false,        areaStyle: { opacity: config.stack === true ? 1 : 0 },        xAxisId: idx,        yAxisId: idx,        data: [dataItem.value[i]]      });    }  }}function renderPie (options, dataItem, idx, coord) {  options.series.push({    name: dataItem.name,    type: 'pie',    width: 50,    height: 80,    left: coord[0] - 30,    top: coord[1] - 40,    radius: ['40%', '70%'],    itemStyle: {      borderRadius: 10,      borderColor: '#fff',      borderWidth: 2    },    label: {      position: 'center',      formatter: dataItem.name,      color: '#617282'    },    emphasis: {      label: {        show: false      }    },    data: dataItem.value.map((item, index) => {      return { 'name': legendArr[index], 'value': item }    })  })}chart.setOption(option);renderEachCity();window.addEventListener('resize', () => {  chart.resize();  renderEachCity();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/d8b77f28.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 迁徙地图</title>
      <link>https://blog.eurkon.com/post/165ef0d3.html</link>
      <guid>https://blog.eurkon.com/post/165ef0d3.html</guid>
      <pubDate>Fri, 15 Oct 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var geoCoordMap = {  '河北': [114.4995, 38.1006],//河北 石家庄  '山西': [112.3352, 37.9413],// 山西 太原  '内蒙古': [111.4124, 40.4901],// 内蒙古 呼和浩特  '辽宁': [123.1238, 42.1216],// 辽宁 沈阳  '吉林': [125.8154, 44.2584],// 吉林 长春  '黑龙江': [127.9688, 45.368],// 黑龙江 哈尔滨  '江苏': [118.8062, 31.9208],//江苏 南京  '浙江': [119.5313, 29.8773],// 浙江 杭州  '安徽': [117.29, 32.0581],// 安徽 合肥  '福建': [119.4543, 25.9222],// 福建 福州  '山东': [117.1582, 36.8701],// 山东 济南  '江西': [116.0046, 28.6633],// 江西 南昌  '河南': [113.4668, 34.6234],//河南 郑州  '新疆': [87.9236, 43.5883],// 新疆 乌鲁木齐  '湖北': [114.3896, 30.6628],// 湖北 武汉  '湖南': [113.0823, 28.2568],// 湖南 长沙  '广东': [113.5107, 23.2196],// 广东 广州  '广西': [108.479, 23.1152],// 广西 南宁  '海南': [110.3893, 19.8516],// 海南 海口  '四川': [103.9526, 30.7617],// 四川 成都  '贵州': [106.6992, 26.7682],// 贵州 贵阳  '云南': [102.9199, 25.4663],// 云南 昆明  '西藏': [91.1865, 30.1465],// 西藏 拉萨  '陕西': [109.1162, 34.2004],// 陕西 西安  '青海': [101.4038, 36.8207],// 青海 西宁  '甘肃': [103.5901, 36.3043],// 甘肃 兰州  '宁夏': [106.3586, 38.1775],// 宁夏 银川  '上海': [121.4648, 31.2891],  '东莞': [113.8953, 22.901],  '东营': [118.7073, 37.5513],  '中山': [113.4229, 22.478],  '临汾': [111.4783, 36.1615],  '临沂': [118.3118, 35.2936],  '丹东': [124.541, 40.4242],  '丽水': [119.5642, 28.1854],  '乌鲁木齐': [87.9236, 43.5883],  '佛山': [112.8955, 23.1097],  '保定': [115.0488, 39.0948],  '兰州': [103.5901, 36.3043],  '包头': [110.3467, 41.4899],  '北京': [116.4551, 40.2539],  '北海': [109.314, 21.6211],  '南京': [118.8062, 31.9208],  '南宁': [108.479, 23.1152],  '南昌': [116.0046, 28.6633],  '南通': [121.1023, 32.1625],  '厦门': [118.1689, 24.6478],  '台州': [121.1353, 28.6688],  '合肥': [117.29, 32.0581],  '呼和浩特': [111.4124, 40.4901],  '咸阳': [108.4131, 34.8706],  '哈尔滨': [127.9688, 45.368],  '唐山': [118.4766, 39.6826],  '嘉兴': [120.9155, 30.6354],  '大同': [113.7854, 39.8035],  '大连': [122.2229, 39.4409],  '天津': [117.4219, 39.4189],  '太原': [112.3352, 37.9413],  '威海': [121.9482, 37.1393],  '宁波': [121.5967, 29.6466],  '宝鸡': [107.1826, 34.3433],  '宿迁': [118.5535, 33.7775],  '常州': [119.4543, 31.5582],  '广州': [113.5107, 23.2196],  '廊坊': [116.521, 39.0509],  '延安': [109.1052, 36.4252],  '张家口': [115.1477, 40.8527],  '徐州': [117.5208, 34.3268],  '德州': [116.6858, 37.2107],  '惠州': [114.6204, 23.1647],  '成都': [103.9526, 30.7617],  '扬州': [119.4653, 32.8162],  '承德': [117.5757, 41.4075],  '拉萨': [91.1865, 30.1465],  '无锡': [120.3442, 31.5527],  '日照': [119.2786, 35.5023],  '昆明': [102.9199, 25.4663],  '杭州': [119.5313, 29.8773],  '枣庄': [117.323, 34.8926],  '柳州': [109.3799, 24.9774],  '株洲': [113.5327, 27.0319],  '武汉': [114.3896, 30.6628],  '汕头': [117.1692, 23.3405],  '江门': [112.6318, 22.1484],  '沈阳': [123.1238, 42.1216],  '沧州': [116.8286, 38.2104],  '河源': [114.917, 23.9722],  '泉州': [118.3228, 25.1147],  '泰安': [117.0264, 36.0516],  '泰州': [120.0586, 32.5525],  '济南': [117.1582, 36.8701],  '济宁': [116.8286, 35.3375],  '海口': [110.3893, 19.8516],  '淄博': [118.0371, 36.6064],  '淮安': [118.927, 33.4039],  '深圳': [114.5435, 22.5439],  '清远': [112.9175, 24.3292],  '温州': [120.498, 27.8119],  '渭南': [109.7864, 35.0299],  '湖州': [119.8608, 30.7782],  '湘潭': [112.5439, 27.7075],  '滨州': [117.8174, 37.4963],  '潍坊': [119.0918, 36.524],  '烟台': [120.7397, 37.5128],  '玉溪': [101.9312, 23.8898],  '珠海': [113.7305, 22.1155],  '盐城': [120.2234, 33.5577],  '盘锦': [121.9482, 41.0449],  '石家庄': [114.4995, 38.1006],  '福州': [119.4543, 25.9222],  '秦皇岛': [119.2126, 40.0232],  '绍兴': [120.564, 29.7565],  '聊城': [115.9167, 36.4032],  '肇庆': [112.1265, 23.5822],  '舟山': [122.2559, 30.2234],  '苏州': [120.6519, 31.3989],  '莱芜': [117.6526, 36.2714],  '菏泽': [115.6201, 35.2057],  '营口': [122.4316, 40.4297],  '葫芦岛': [120.1575, 40.578],  '衡水': [115.8838, 37.7161],  '衢州': [118.6853, 28.8666],  '西宁': [101.4038, 36.8207],  '西安': [109.1162, 34.2004],  '贵阳': [106.6992, 26.7682],  '连云港': [119.1248, 34.552],  '邢台': [114.8071, 37.2821],  '邯郸': [114.4775, 36.535],  '郑州': [113.4668, 34.6234],  '鄂尔多斯': [108.9734, 39.2487],  '重庆': [107.7539, 30.1904],  '金华': [120.0037, 29.1028],  '铜川': [109.0393, 35.1947],  '银川': [106.3586, 38.1775],  '镇江': [119.4763, 31.9702],  '长春': [125.8154, 44.2584],  '长沙': [113.0823, 28.2568],  '长治': [112.8625, 36.4746],  '阳泉': [113.4778, 38.0951],  '青岛': [120.4651, 36.3373],  '韶关': [113.7964, 24.7028]};var data = [  {    'name': '城市',    'data': [      '北京',      '北京',      '北京',      '北京',      '北京',      '上海',      '上海',      '上海',      '上海',      '广东',      '广东',      '广东',      '广东',      '广东'    ]  },  {    'name': '城市',    'data': [      '山西',      '广东',      '浙江',      '山东',      '上海',      '陕西',      '海南',      '沈阳',      '北京',      '江苏',      '大连',      '吉林',      '广西',      '四川'    ]  },  {    'name': '促销数量',    'data': [      4948,      2698,      2264,      2734,      4951,      4949,      2728,      1854,      6821,      1838,      1846,      1842,      2254,      2716    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar seriesList = []for (let i = 0; i < rowNum; i++) {  seriesList.push([{ 'name': data[0].data[i] }, { 'name': data[1].data[i], 'value': data[2].data[i] }])}var option = {  top: '-40%',  bottom: '-40%',  tooltip: { trigger: 'item' },  visualMap: {    show: true,    top: 'bottom',    min: 0,    max: Math.max(...seriesList.map(item => { return item[1].value })),    text: ['High', 'Low'],    textStyle: {      color: config.theme === 'DARK' ? '#B4C2D6' : '#617282'    },    inRange: {      color: ['#a3ccf8', '#8fb8ee', '#7ca4e3', '#6890d9', '#547cce']    }  },  geo: {    type: 'map',    map: 'china',    label: {      normal: { show: false },      emphasis: { show: true, color: '#617282' }    },    itemStyle: {      normal: {        areaColor: 'rgb(230, 232, 234)',        borderColor: 'rgb(255, 255, 255)',        borderWidth: 1      },      emphasis: {        areaColor: 'rgb(186, 218, 85)'      }    }  },  series: [    {      name: data[2].name,      type: 'effectScatter',      coordinateSystem: 'geo',      tooltip: {        formatter: function (params) {          return params.data.name + '<br>' + params.marker + params.seriesName + '：' + params.data.value[2]        }      },      symbolSize: function (val) {        return val[2] * 15 / Math.max(...seriesList.map(item => { return item[1].value }))      },      rippleEffect: { brushType: 'stroke' },      data: seriesList.map(item => {        return {          name: item[1].name,          value: geoCoordMap[item[1].name].concat(item[1].value)        }      })    },    {      name: data[2].name,      tooltip: {        formatter: function (params) {          return 'from ' + params.data.fromName + ' to ' + params.data.toName + '<br>' + params.marker + params.seriesName + '：' + params.data.value;        }      },      type: 'lines',      effect: {        show: true,        period: 2, //箭头指向速度，值越小速度越快        trailLength: 0.2, //特效尾迹[0,1]值越大，尾迹越长        symbol: 'arrow', //箭头图标        symbolSize: 5 //图标大小      },      lineStyle: {        width: 2,        opacity: 0.3,        curveness: 0.2      },      data: seriesList.map(item => {        return {          fromName: item[0].name,          toName: item[1].name,          coords: [            geoCoordMap[item[0].name],            geoCoordMap[item[1].name]          ],          value: item[1].value        }      }),    },  ]};chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/165ef0d3.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 标签地图</title>
      <link>https://blog.eurkon.com/post/19a77adb.html</link>
      <guid>https://blog.eurkon.com/post/19a77adb.html</guid>
      <pubDate>Fri, 01 Oct 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var geoCoordMap = {  '河北': [114.4995, 38.1006],//河北 石家庄  '山西': [112.3352, 37.9413],// 山西 太原  '内蒙古': [111.4124, 40.4901],// 内蒙古 呼和浩特  '辽宁': [123.1238, 42.1216],// 辽宁 沈阳  '吉林': [125.8154, 44.2584],// 吉林 长春  '黑龙江': [127.9688, 45.368],// 黑龙江 哈尔滨  '江苏': [118.8062, 31.9208],//江苏 南京  '浙江': [119.5313, 29.8773],// 浙江 杭州  '安徽': [117.29, 32.0581],// 安徽 合肥  '福建': [119.4543, 25.9222],// 福建 福州  '山东': [117.1582, 36.8701],// 山东 济南  '江西': [116.0046, 28.6633],// 江西 南昌  '河南': [113.4668, 34.6234],//河南 郑州  '新疆': [87.9236, 43.5883],// 新疆 乌鲁木齐  '湖北': [114.3896, 30.6628],// 湖北 武汉  '湖南': [113.0823, 28.2568],// 湖南 长沙  '广东': [113.5107, 23.2196],// 广东 广州  '广西': [108.479, 23.1152],// 广西 南宁  '海南': [110.3893, 19.8516],// 海南 海口  '四川': [103.9526, 30.7617],// 四川 成都  '贵州': [106.6992, 26.7682],// 贵州 贵阳  '云南': [102.9199, 25.4663],// 云南 昆明  '西藏': [91.1865, 30.1465],// 西藏 拉萨  '陕西': [109.1162, 34.2004],// 陕西 西安  '青海': [101.4038, 36.8207],// 青海 西宁  '甘肃': [103.5901, 36.3043],// 甘肃 兰州  '宁夏': [106.3586, 38.1775],// 宁夏 银川  '上海': [121.4648, 31.2891],  '东莞': [113.8953, 22.901],  '东营': [118.7073, 37.5513],  '中山': [113.4229, 22.478],  '临汾': [111.4783, 36.1615],  '临沂': [118.3118, 35.2936],  '丹东': [124.541, 40.4242],  '丽水': [119.5642, 28.1854],  '乌鲁木齐': [87.9236, 43.5883],  '佛山': [112.8955, 23.1097],  '保定': [115.0488, 39.0948],  '兰州': [103.5901, 36.3043],  '包头': [110.3467, 41.4899],  '北京': [116.4551, 40.2539],  '北海': [109.314, 21.6211],  '南京': [118.8062, 31.9208],  '南宁': [108.479, 23.1152],  '南昌': [116.0046, 28.6633],  '南通': [121.1023, 32.1625],  '厦门': [118.1689, 24.6478],  '台州': [121.1353, 28.6688],  '合肥': [117.29, 32.0581],  '呼和浩特': [111.4124, 40.4901],  '咸阳': [108.4131, 34.8706],  '哈尔滨': [127.9688, 45.368],  '唐山': [118.4766, 39.6826],  '嘉兴': [120.9155, 30.6354],  '大同': [113.7854, 39.8035],  '大连': [122.2229, 39.4409],  '天津': [117.4219, 39.4189],  '太原': [112.3352, 37.9413],  '威海': [121.9482, 37.1393],  '宁波': [121.5967, 29.6466],  '宝鸡': [107.1826, 34.3433],  '宿迁': [118.5535, 33.7775],  '常州': [119.4543, 31.5582],  '广州': [113.5107, 23.2196],  '廊坊': [116.521, 39.0509],  '延安': [109.1052, 36.4252],  '张家口': [115.1477, 40.8527],  '徐州': [117.5208, 34.3268],  '德州': [116.6858, 37.2107],  '惠州': [114.6204, 23.1647],  '成都': [103.9526, 30.7617],  '扬州': [119.4653, 32.8162],  '承德': [117.5757, 41.4075],  '拉萨': [91.1865, 30.1465],  '无锡': [120.3442, 31.5527],  '日照': [119.2786, 35.5023],  '昆明': [102.9199, 25.4663],  '杭州': [119.5313, 29.8773],  '枣庄': [117.323, 34.8926],  '柳州': [109.3799, 24.9774],  '株洲': [113.5327, 27.0319],  '武汉': [114.3896, 30.6628],  '汕头': [117.1692, 23.3405],  '江门': [112.6318, 22.1484],  '沈阳': [123.1238, 42.1216],  '沧州': [116.8286, 38.2104],  '河源': [114.917, 23.9722],  '泉州': [118.3228, 25.1147],  '泰安': [117.0264, 36.0516],  '泰州': [120.0586, 32.5525],  '济南': [117.1582, 36.8701],  '济宁': [116.8286, 35.3375],  '海口': [110.3893, 19.8516],  '淄博': [118.0371, 36.6064],  '淮安': [118.927, 33.4039],  '深圳': [114.5435, 22.5439],  '清远': [112.9175, 24.3292],  '温州': [120.498, 27.8119],  '渭南': [109.7864, 35.0299],  '湖州': [119.8608, 30.7782],  '湘潭': [112.5439, 27.7075],  '滨州': [117.8174, 37.4963],  '潍坊': [119.0918, 36.524],  '烟台': [120.7397, 37.5128],  '玉溪': [101.9312, 23.8898],  '珠海': [113.7305, 22.1155],  '盐城': [120.2234, 33.5577],  '盘锦': [121.9482, 41.0449],  '石家庄': [114.4995, 38.1006],  '福州': [119.4543, 25.9222],  '秦皇岛': [119.2126, 40.0232],  '绍兴': [120.564, 29.7565],  '聊城': [115.9167, 36.4032],  '肇庆': [112.1265, 23.5822],  '舟山': [122.2559, 30.2234],  '苏州': [120.6519, 31.3989],  '莱芜': [117.6526, 36.2714],  '菏泽': [115.6201, 35.2057],  '营口': [122.4316, 40.4297],  '葫芦岛': [120.1575, 40.578],  '衡水': [115.8838, 37.7161],  '衢州': [118.6853, 28.8666],  '西宁': [101.4038, 36.8207],  '西安': [109.1162, 34.2004],  '贵阳': [106.6992, 26.7682],  '连云港': [119.1248, 34.552],  '邢台': [114.8071, 37.2821],  '邯郸': [114.4775, 36.535],  '郑州': [113.4668, 34.6234],  '鄂尔多斯': [108.9734, 39.2487],  '重庆': [107.7539, 30.1904],  '金华': [120.0037, 29.1028],  '铜川': [109.0393, 35.1947],  '银川': [106.3586, 38.1775],  '镇江': [119.4763, 31.9702],  '长春': [125.8154, 44.2584],  '长沙': [113.0823, 28.2568],  '长治': [112.8625, 36.4746],  '阳泉': [113.4778, 38.0951],  '青岛': [120.4651, 36.3373],  '韶关': [113.7964, 24.7028]};var data = [  {    'name': '城市',    'data': [      '山西',      '广东',      '浙江',      '山东',      '上海',      '陕西',      '海南',      '沈阳',      '河北',      '江苏',      '大连',      '吉林',      '广西',      '四川'    ]  },  {    'name': '促销数量',    'data': [      4948,      2698,      2264,      2734,      4951,      4949,      2728,      1854,      6821,      1838,      1846,      1842,      2254,      2716    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar seriesList = []for (let i = 0; i < rowNum; i++) {  seriesList.push({ 'name': data[0].data[i], 'value': data[1].data[i] })}var option = {  top: '-40%',  bottom: '-40%',  geo: {    type: 'map',    map: 'china',    label: {      normal: { show: false },      emphasis: { show: true, color: '#617282' }    },    itemStyle: {      normal: {        areaColor: 'rgb(230, 232, 234)',        borderColor: 'rgb(255, 255, 255)',        borderWidth: 1      },      emphasis: {        areaColor: 'rgb(186, 218, 85)'      }    }  },  series: [    {      name: data[1].name,      type: 'effectScatter',      coordinateSystem: 'geo',      symbolSize: 0,      shadowBlur: 0,      label: {        normal: {          show: true,          formatter: function (params) {            return '{div|' + params.name + '}\n{font|' + params.value[2] + '}'          },          rich: {            font: {              height: 10,              color: '#ffffff',              fontSize: 13,              fontWeight: 900,              padding: [0, 4, 4, 4] //上, 右, 下, 左            },            div: {              height: 22,              fontSize: 12,              lineHeight: 10,              padding: [0, 8, 16, 8],              color: config.colors[0],              borderRadius: 8,              backgroundColor: 'rgba(127, 127, 127, .5)'            }          }        }      },      data: seriesList.map(item => {        return {          name: item.name,          value: geoCoordMap[item.name].concat(item.value)        }      })    }  ]};chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/19a77adb.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 热力地图</title>
      <link>https://blog.eurkon.com/post/85c31989.html</link>
      <guid>https://blog.eurkon.com/post/85c31989.html</guid>
      <pubDate>Wed, 15 Sep 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var geoCoordMap = {  '河北': [114.4995, 38.1006],//河北 石家庄  '山西': [112.3352, 37.9413],// 山西 太原  '内蒙古': [111.4124, 40.4901],// 内蒙古 呼和浩特  '辽宁': [123.1238, 42.1216],// 辽宁 沈阳  '吉林': [125.8154, 44.2584],// 吉林 长春  '黑龙江': [127.9688, 45.368],// 黑龙江 哈尔滨  '江苏': [118.8062, 31.9208],//江苏 南京  '浙江': [119.5313, 29.8773],// 浙江 杭州  '安徽': [117.29, 32.0581],// 安徽 合肥  '福建': [119.4543, 25.9222],// 福建 福州  '山东': [117.1582, 36.8701],// 山东 济南  '江西': [116.0046, 28.6633],// 江西 南昌  '河南': [113.4668, 34.6234],//河南 郑州  '新疆': [87.9236, 43.5883],// 新疆 乌鲁木齐  '湖北': [114.3896, 30.6628],// 湖北 武汉  '湖南': [113.0823, 28.2568],// 湖南 长沙  '广东': [113.5107, 23.2196],// 广东 广州  '广西': [108.479, 23.1152],// 广西 南宁  '海南': [110.3893, 19.8516],// 海南 海口  '四川': [103.9526, 30.7617],// 四川 成都  '贵州': [106.6992, 26.7682],// 贵州 贵阳  '云南': [102.9199, 25.4663],// 云南 昆明  '西藏': [91.1865, 30.1465],// 西藏 拉萨  '陕西': [109.1162, 34.2004],// 陕西 西安  '青海': [101.4038, 36.8207],// 青海 西宁  '甘肃': [103.5901, 36.3043],// 甘肃 兰州  '宁夏': [106.3586, 38.1775],// 宁夏 银川  '上海': [121.4648, 31.2891],  '东莞': [113.8953, 22.901],  '东营': [118.7073, 37.5513],  '中山': [113.4229, 22.478],  '临汾': [111.4783, 36.1615],  '临沂': [118.3118, 35.2936],  '丹东': [124.541, 40.4242],  '丽水': [119.5642, 28.1854],  '乌鲁木齐': [87.9236, 43.5883],  '佛山': [112.8955, 23.1097],  '保定': [115.0488, 39.0948],  '兰州': [103.5901, 36.3043],  '包头': [110.3467, 41.4899],  '北京': [116.4551, 40.2539],  '北海': [109.314, 21.6211],  '南京': [118.8062, 31.9208],  '南宁': [108.479, 23.1152],  '南昌': [116.0046, 28.6633],  '南通': [121.1023, 32.1625],  '厦门': [118.1689, 24.6478],  '台州': [121.1353, 28.6688],  '合肥': [117.29, 32.0581],  '呼和浩特': [111.4124, 40.4901],  '咸阳': [108.4131, 34.8706],  '哈尔滨': [127.9688, 45.368],  '唐山': [118.4766, 39.6826],  '嘉兴': [120.9155, 30.6354],  '大同': [113.7854, 39.8035],  '大连': [122.2229, 39.4409],  '天津': [117.4219, 39.4189],  '太原': [112.3352, 37.9413],  '威海': [121.9482, 37.1393],  '宁波': [121.5967, 29.6466],  '宝鸡': [107.1826, 34.3433],  '宿迁': [118.5535, 33.7775],  '常州': [119.4543, 31.5582],  '广州': [113.5107, 23.2196],  '廊坊': [116.521, 39.0509],  '延安': [109.1052, 36.4252],  '张家口': [115.1477, 40.8527],  '徐州': [117.5208, 34.3268],  '德州': [116.6858, 37.2107],  '惠州': [114.6204, 23.1647],  '成都': [103.9526, 30.7617],  '扬州': [119.4653, 32.8162],  '承德': [117.5757, 41.4075],  '拉萨': [91.1865, 30.1465],  '无锡': [120.3442, 31.5527],  '日照': [119.2786, 35.5023],  '昆明': [102.9199, 25.4663],  '杭州': [119.5313, 29.8773],  '枣庄': [117.323, 34.8926],  '柳州': [109.3799, 24.9774],  '株洲': [113.5327, 27.0319],  '武汉': [114.3896, 30.6628],  '汕头': [117.1692, 23.3405],  '江门': [112.6318, 22.1484],  '沈阳': [123.1238, 42.1216],  '沧州': [116.8286, 38.2104],  '河源': [114.917, 23.9722],  '泉州': [118.3228, 25.1147],  '泰安': [117.0264, 36.0516],  '泰州': [120.0586, 32.5525],  '济南': [117.1582, 36.8701],  '济宁': [116.8286, 35.3375],  '海口': [110.3893, 19.8516],  '淄博': [118.0371, 36.6064],  '淮安': [118.927, 33.4039],  '深圳': [114.5435, 22.5439],  '清远': [112.9175, 24.3292],  '温州': [120.498, 27.8119],  '渭南': [109.7864, 35.0299],  '湖州': [119.8608, 30.7782],  '湘潭': [112.5439, 27.7075],  '滨州': [117.8174, 37.4963],  '潍坊': [119.0918, 36.524],  '烟台': [120.7397, 37.5128],  '玉溪': [101.9312, 23.8898],  '珠海': [113.7305, 22.1155],  '盐城': [120.2234, 33.5577],  '盘锦': [121.9482, 41.0449],  '石家庄': [114.4995, 38.1006],  '福州': [119.4543, 25.9222],  '秦皇岛': [119.2126, 40.0232],  '绍兴': [120.564, 29.7565],  '聊城': [115.9167, 36.4032],  '肇庆': [112.1265, 23.5822],  '舟山': [122.2559, 30.2234],  '苏州': [120.6519, 31.3989],  '莱芜': [117.6526, 36.2714],  '菏泽': [115.6201, 35.2057],  '营口': [122.4316, 40.4297],  '葫芦岛': [120.1575, 40.578],  '衡水': [115.8838, 37.7161],  '衢州': [118.6853, 28.8666],  '西宁': [101.4038, 36.8207],  '西安': [109.1162, 34.2004],  '贵阳': [106.6992, 26.7682],  '连云港': [119.1248, 34.552],  '邢台': [114.8071, 37.2821],  '邯郸': [114.4775, 36.535],  '郑州': [113.4668, 34.6234],  '鄂尔多斯': [108.9734, 39.2487],  '重庆': [107.7539, 30.1904],  '金华': [120.0037, 29.1028],  '铜川': [109.0393, 35.1947],  '银川': [106.3586, 38.1775],  '镇江': [119.4763, 31.9702],  '长春': [125.8154, 44.2584],  '长沙': [113.0823, 28.2568],  '长治': [112.8625, 36.4746],  '阳泉': [113.4778, 38.0951],  '青岛': [120.4651, 36.3373],  '韶关': [113.7964, 24.7028]};var data = [  {    'name': '城市',    'data': [      '山西',      '广东',      '浙江',      '山东',      '上海',      '陕西',      '海南',      '沈阳',      '河北',      '江苏',      '大连',      '吉林',      '广西',      '四川'    ]  },  {    'name': '促销数量',    'data': [      4948,      2698,      2264,      2734,      4951,      4949,      2728,      1854,      6821,      1838,      1846,      1842,      2254,      2716    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar seriesList = []for (let i = 0; i < rowNum; i++) {  seriesList.push({ 'name': data[0].data[i], 'value': data[1].data[i] })}var option = {  top: '-40%',  bottom: '-40%',  color: config.colors,  visualMap: {    show: true,    top: 'bottom',    min: 0,    max: Math.max(...seriesList.map(item => { return item.value })),    text: ['High', 'Low'],    textStyle: {      color: config.theme === 'DARK' ? '#B4C2D6' : '#617282'    },    inRange: {      color: ['#a3ccf8', '#8fb8ee', '#7ca4e3', '#6890d9', '#547cce']    }  },  geo: {    type: 'map',    map: 'china',    label: {      normal: { show: false },      emphasis: { show: true, color: '#617282' }    },    itemStyle: {      normal: {        areaColor: 'rgb(230, 232, 234)',        borderColor: 'rgb(255, 255, 255)',        borderWidth: 1      },      emphasis: {        areaColor: 'rgb(186, 218, 85)'      }    }  },  series: [    {      name: data[1].name,      type: 'heatmap',      coordinateSystem: 'geo',      data: seriesList.map(item => {        return {          name: item.name,          value: geoCoordMap[item.name].concat(item.value)        }      })    }  ]};chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/85c31989.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 散点地图</title>
      <link>https://blog.eurkon.com/post/e3dd705b.html</link>
      <guid>https://blog.eurkon.com/post/e3dd705b.html</guid>
      <pubDate>Wed, 01 Sep 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var geoCoordMap = {  '河北': [114.4995, 38.1006],//河北 石家庄  '山西': [112.3352, 37.9413],// 山西 太原  '内蒙古': [111.4124, 40.4901],// 内蒙古 呼和浩特  '辽宁': [123.1238, 42.1216],// 辽宁 沈阳  '吉林': [125.8154, 44.2584],// 吉林 长春  '黑龙江': [127.9688, 45.368],// 黑龙江 哈尔滨  '江苏': [118.8062, 31.9208],//江苏 南京  '浙江': [119.5313, 29.8773],// 浙江 杭州  '安徽': [117.29, 32.0581],// 安徽 合肥  '福建': [119.4543, 25.9222],// 福建 福州  '山东': [117.1582, 36.8701],// 山东 济南  '江西': [116.0046, 28.6633],// 江西 南昌  '河南': [113.4668, 34.6234],//河南 郑州  '新疆': [87.9236, 43.5883],// 新疆 乌鲁木齐  '湖北': [114.3896, 30.6628],// 湖北 武汉  '湖南': [113.0823, 28.2568],// 湖南 长沙  '广东': [113.5107, 23.2196],// 广东 广州  '广西': [108.479, 23.1152],// 广西 南宁  '海南': [110.3893, 19.8516],// 海南 海口  '四川': [103.9526, 30.7617],// 四川 成都  '贵州': [106.6992, 26.7682],// 贵州 贵阳  '云南': [102.9199, 25.4663],// 云南 昆明  '西藏': [91.1865, 30.1465],// 西藏 拉萨  '陕西': [109.1162, 34.2004],// 陕西 西安  '青海': [101.4038, 36.8207],// 青海 西宁  '甘肃': [103.5901, 36.3043],// 甘肃 兰州  '宁夏': [106.3586, 38.1775],// 宁夏 银川  '上海': [121.4648, 31.2891],  '东莞': [113.8953, 22.901],  '东营': [118.7073, 37.5513],  '中山': [113.4229, 22.478],  '临汾': [111.4783, 36.1615],  '临沂': [118.3118, 35.2936],  '丹东': [124.541, 40.4242],  '丽水': [119.5642, 28.1854],  '乌鲁木齐': [87.9236, 43.5883],  '佛山': [112.8955, 23.1097],  '保定': [115.0488, 39.0948],  '兰州': [103.5901, 36.3043],  '包头': [110.3467, 41.4899],  '北京': [116.4551, 40.2539],  '北海': [109.314, 21.6211],  '南京': [118.8062, 31.9208],  '南宁': [108.479, 23.1152],  '南昌': [116.0046, 28.6633],  '南通': [121.1023, 32.1625],  '厦门': [118.1689, 24.6478],  '台州': [121.1353, 28.6688],  '合肥': [117.29, 32.0581],  '呼和浩特': [111.4124, 40.4901],  '咸阳': [108.4131, 34.8706],  '哈尔滨': [127.9688, 45.368],  '唐山': [118.4766, 39.6826],  '嘉兴': [120.9155, 30.6354],  '大同': [113.7854, 39.8035],  '大连': [122.2229, 39.4409],  '天津': [117.4219, 39.4189],  '太原': [112.3352, 37.9413],  '威海': [121.9482, 37.1393],  '宁波': [121.5967, 29.6466],  '宝鸡': [107.1826, 34.3433],  '宿迁': [118.5535, 33.7775],  '常州': [119.4543, 31.5582],  '广州': [113.5107, 23.2196],  '廊坊': [116.521, 39.0509],  '延安': [109.1052, 36.4252],  '张家口': [115.1477, 40.8527],  '徐州': [117.5208, 34.3268],  '德州': [116.6858, 37.2107],  '惠州': [114.6204, 23.1647],  '成都': [103.9526, 30.7617],  '扬州': [119.4653, 32.8162],  '承德': [117.5757, 41.4075],  '拉萨': [91.1865, 30.1465],  '无锡': [120.3442, 31.5527],  '日照': [119.2786, 35.5023],  '昆明': [102.9199, 25.4663],  '杭州': [119.5313, 29.8773],  '枣庄': [117.323, 34.8926],  '柳州': [109.3799, 24.9774],  '株洲': [113.5327, 27.0319],  '武汉': [114.3896, 30.6628],  '汕头': [117.1692, 23.3405],  '江门': [112.6318, 22.1484],  '沈阳': [123.1238, 42.1216],  '沧州': [116.8286, 38.2104],  '河源': [114.917, 23.9722],  '泉州': [118.3228, 25.1147],  '泰安': [117.0264, 36.0516],  '泰州': [120.0586, 32.5525],  '济南': [117.1582, 36.8701],  '济宁': [116.8286, 35.3375],  '海口': [110.3893, 19.8516],  '淄博': [118.0371, 36.6064],  '淮安': [118.927, 33.4039],  '深圳': [114.5435, 22.5439],  '清远': [112.9175, 24.3292],  '温州': [120.498, 27.8119],  '渭南': [109.7864, 35.0299],  '湖州': [119.8608, 30.7782],  '湘潭': [112.5439, 27.7075],  '滨州': [117.8174, 37.4963],  '潍坊': [119.0918, 36.524],  '烟台': [120.7397, 37.5128],  '玉溪': [101.9312, 23.8898],  '珠海': [113.7305, 22.1155],  '盐城': [120.2234, 33.5577],  '盘锦': [121.9482, 41.0449],  '石家庄': [114.4995, 38.1006],  '福州': [119.4543, 25.9222],  '秦皇岛': [119.2126, 40.0232],  '绍兴': [120.564, 29.7565],  '聊城': [115.9167, 36.4032],  '肇庆': [112.1265, 23.5822],  '舟山': [122.2559, 30.2234],  '苏州': [120.6519, 31.3989],  '莱芜': [117.6526, 36.2714],  '菏泽': [115.6201, 35.2057],  '营口': [122.4316, 40.4297],  '葫芦岛': [120.1575, 40.578],  '衡水': [115.8838, 37.7161],  '衢州': [118.6853, 28.8666],  '西宁': [101.4038, 36.8207],  '西安': [109.1162, 34.2004],  '贵阳': [106.6992, 26.7682],  '连云港': [119.1248, 34.552],  '邢台': [114.8071, 37.2821],  '邯郸': [114.4775, 36.535],  '郑州': [113.4668, 34.6234],  '鄂尔多斯': [108.9734, 39.2487],  '重庆': [107.7539, 30.1904],  '金华': [120.0037, 29.1028],  '铜川': [109.0393, 35.1947],  '银川': [106.3586, 38.1775],  '镇江': [119.4763, 31.9702],  '长春': [125.8154, 44.2584],  '长沙': [113.0823, 28.2568],  '长治': [112.8625, 36.4746],  '阳泉': [113.4778, 38.0951],  '青岛': [120.4651, 36.3373],  '韶关': [113.7964, 24.7028]};var data = [  {    'name': '城市',    'data': [      '山西',      '广东',      '浙江',      '山东',      '上海',      '陕西',      '海南',      '沈阳',      '河北',      '江苏',      '大连',      '吉林',      '广西',      '四川'    ]  },  {    'name': '促销数量',    'data': [      4948,      2698,      2264,      2734,      4951,      4949,      2728,      1854,      6821,      1838,      1846,      1842,      2254,      2716    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar seriesList = []for (let i = 0; i < rowNum; i++) {  seriesList.push({ 'name': data[0].data[i], 'value': data[1].data[i] })}var option = {  top: '-40%',  bottom: '-40%',  tooltip: { trigger: 'item' },  visualMap: {    show: true,    top: 'bottom',    min: 0,    max: Math.max(...seriesList.map(item => { return item.value })),    text: ['High', 'Low'],    textStyle: {      color: config.theme === 'DARK' ? '#B4C2D6' : '#617282'    },    inRange: {      color: ['#a3ccf8', '#8fb8ee', '#7ca4e3', '#6890d9', '#547cce']    }  },  geo: {    type: 'map',    map: 'china',    label: {      normal: { show: false },      emphasis: { show: true, color: '#617282' }    },    itemStyle: {      normal: {        areaColor: 'rgb(230, 232, 234)',        borderColor: 'rgb(255, 255, 255)',        borderWidth: 1      },      emphasis: {        areaColor: 'rgb(186, 218, 85)'      }    }  },  series: [    {      name: data[1].name,      type: 'effectScatter',      coordinateSystem: 'geo',      tooltip: {        formatter: function (params) {          return params.data.name + '<br>' + params.marker + params.seriesName + '：' + params.data.value[2]        }      },      symbolSize: function (val) {        return val[2] * 20 / Math.max(...seriesList.map(item => { return item.value }))      },      rippleEffect: {        brushType: 'stroke'      },      data: seriesList.map(item => {        return {          name: item.name,          value: geoCoordMap[item.name].concat(item.value)        }      })    }  ]};chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/e3dd705b.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 同X轴多Y轴图表</title>
      <link>https://blog.eurkon.com/post/acc26f11.html</link>
      <guid>https://blog.eurkon.com/post/acc26f11.html</guid>
      <pubDate>Mon, 30 Aug 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': '日期',    'data': [      '2021-01-01',      '2021-01-02',      '2021-01-03',      '2021-01-04',      '2021-01-05',      '2021-01-06',      '2021-01-07',      '2021-01-08',      '2021-01-09',      '2021-01-10',      '2021-01-11',      '2021-01-12',      '2021-01-13',      '2021-01-14',      '2021-02-01',      '2021-02-02',      '2021-02-03',      '2021-02-04',      '2021-02-05',      '2021-02-06',      '2021-02-07',      '2021-02-08',      '2021-02-09',      '2021-02-10',      '2021-02-11',      '2021-02-12',      '2021-02-13',      '2021-02-14',      '2021-03-01',      '2021-03-02',      '2021-03-03',      '2021-03-04',      '2021-03-05',      '2021-03-06',      '2021-03-07',      '2021-03-08',      '2021-03-09',      '2021-03-10',      '2021-03-11',      '2021-03-12',      '2021-03-13',      '2021-03-14',      '2021-03-15'    ]  },  {    'name': '采购数量',    'data': [      20000,      19998,      19996,      19994,      19992,      19990,      19988,      19986,      19984,      19982,      19980,      19978,      19976,      19974,      19972,      19970,      19968,      19966,      19964,      19962,      19960,      19958,      19956,      19954,      19952,      19950,      19948,      19946,      19944,      19942,      19940,      19938,      19936,      19934,      19932,      19930,      19928,      19926,      19924,      19922,      19920,      19918,      19916    ]  },  {    'name': '入库数量',    'data': [      18000,      17850,      17700,      17680,      17660,      17640,      17620,      17600,      17580,      17560,      17540,      17520,      17500,      17480,      17460,      17440,      17420,      17400,      17380,      17360,      17340,      17320,      17300,      17280,      17260,      17240,      17220,      17200,      17180,      17160,      17140,      17120,      17100,      17080,      17060,      17040,      17020,      17000,      16980,      16960,      16940,      16920,      16900    ]  },  {    'name': '促销数量',    'data': [      500,      499,      498,      497,      496,      495,      494,      493,      492,      491,      490,      489,      488,      487,      486,      485,      484,      483,      482,      481,      480,      479,      478,      477,      476,      475,      474,      473,      472,      471,      470,      469,      468,      467,      466,      465,      464,      463,      462,      461,      460,      459,      458    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  typeList: ['line', 'bar', 'line'], // 图表类型列表  scale: true // Y轴是否是脱离0值。}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar baseTop = 100 / (colNum - 1);var gridHeight = 100 / (colNum - 1);var option = {  color: config.colors,  tooltip: {    trigger: 'axis',    formatter: function (params) {      let res = [params[0].name]      params.forEach(item => {        res.push(item.marker + item.seriesName + '：' + item.value)      })      return res.join('<br>');    }  },  textStyle: { color: config.theme === 'DARK' ? '#B4C2D6' : '#617282' },  lineStyle: { color: config.theme === 'DARK' ? '#434961' : '#dbdfe7' },  axisPointer: {    link: [{ xAxisIndex: 'all' }],    snap: true,  },  grid: [],  xAxis: [],  yAxis: [],  series: []};var seriesNum = colNum - 1for (let i = 0; i < seriesNum; i++) {  idx = i + '';  option.grid.push({    top: baseTop * i + seriesNum + '%',    height: gridHeight - seriesNum ** 2 + '%',    left: '10%',    right: '5%'  })  option.xAxis.push({    id: idx,    gridIndex: idx,    axisLine: { show: true },    axisTick: { show: true },    splitLine: { show: false },    axisLabel: { show: i === seriesNum - 1 ? true : false },    data: data[0].data  })  option.yAxis.push({    name: data[i + 1].name,    gridIndex: idx,    scale: config.scale,    nameLocation: 'middle',    boundaryGap: ['30%', '30%'],    axisTick: { show: false },    axisLine: { show: false },    axisLabel: { show: true },    splitLine: { show: true },    nameTextStyle: {      padding: 35    }  })  option.series.push({    name: data[i + 1].name,    type: config.typeList[i] || 'line',    xAxisIndex: idx,    yAxisIndex: idx,    symbol: 'emptyCircle',    symbolSize: 5,    lineStyle: { color: config.colors[i] },    data: data[i + 1].data,  })}chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/acc26f11.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 弹窗悬浮图表</title>
      <link>https://blog.eurkon.com/post/8050767e.html</link>
      <guid>https://blog.eurkon.com/post/8050767e.html</guid>
      <pubDate>Sun, 15 Aug 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 500px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 500px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': '日期 (月)',    'data': [      '2020-01',      '2020-02',      '2020-03',      '2020-04',      '2020-05',      '2020-06'    ]  },  {    'name': '采购数量',    'data': [      279818,      279426,      279034,      278642,      278250,      255716    ]  },  {    'name': '入库数量',    'data': [      246930,      242620,      238700,      234780,      230860,      223880    ]  },  {    'name': '配送数量',    'data': [      152180,      148260,      144340,      140420,      136500,      125160    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthfunction render (data) {  var tooltipContainer = document.getElementById('tooltip-container')   tooltipChart = echarts.getInstanceByDom(tooltipContainer) || echarts.init(tooltipContainer, null, { renderer: 'svg' })  var tooltipOption = {    color: config.colors,    legend: { show: false, data: legendList },    series: [      {        name: '',        type: 'pie',        radius: '50%',        label: { formatter: '{c}\n{d}%' },        data: data      }    ]  }  tooltipChart.setOption(tooltipOption)}var option = {  color: config.colors,  legend: {    data: legendList,    textStyle: { color: config.theme === 'DARK' ? '#B4C2D6' : '#617282' }  },  tooltip: {    trigger: 'axis',    backgroundColor: '#fff',    borderColor: config.theme === 'DARK' ? '#B4C2D6' : '#617282',    borderWidth: 1,    formatter: function (params) {      setTimeout(function () {        var tooltipData = params.map(item => {          return { 'name': item.seriesName, 'value': item.value }        })        render(tooltipData);      }, 100)      return '<div id="tooltip-container" style="width: 240px; height: 100px;"></div>';    },  },  textStyle: { color: config.theme === 'DARK' ? '#B4C2D6' : '#617282' },  lineStyle: { color: config.theme === 'DARK' ? '#434961' : '#dbdfe7' },  xAxis: {    type: 'category',    axisTick: { show: true },    axisLine: { show: true },    splitLine: { show: false },    axisLabel: { show: true },    data: data[0].data  },  yAxis: {    type: 'value',    axisTick: { show: false },    axisLine: { show: false },    splitLine: { show: true },    axisLabel: { show: true },  },  series: []};var legendList = []for (var i = 1; i < colNum; i++) {  legendList.push(data[i].name)  option.series.push({    name: data[i].name,    type: 'line',    lineStyle: { color: config.colors[i - 1] },    symbol: 'emptyCircle',    symbolSize: 6,    data: data[i].data,  })}chart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/8050767e.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ECharts 时序图</title>
      <link>https://blog.eurkon.com/post/b9e0457b.html</link>
      <guid>https://blog.eurkon.com/post/b9e0457b.html</guid>
      <pubDate>Sun, 01 Aug 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;
  #container {
    height: 300px;
    padding: 10px;
    background: white;
    border-radius: 12px;
  }
&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;&lt;div i</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>  #container {    height: 300px;    padding: 10px;    background: white;    border-radius: 12px;  }</style></p><p><div id="container"></div></p><p><script defer data-pjax>var data = [  {    'name': '时间',    'data': [      '2021-01',      '2021-03',      '2021-05',      '2021-07',      '2021-09',      '2021-11',      '2022-01'    ]  },  {    'name': '地点',    'data': [      '地点1',      '地点2',      '地点3',      '地点4',      '地点5',      '地点6',      '地点7'    ]  },  {    'name': '标题',    'data': [      '标题1标题1标题1',      '标题2标题2标题2',      '标题3标题3标题3',      '标题4标题4标题4',      '标题5标题5标题5',      '标题6标题6标题6',      '标题7标题7标题7'    ]  },  {    'name': '内容',    'data': [      '内容1内容1内容1内容1内容1内容1内容1内容1内容1内容1',      '内容2内容2内容2内容2内容2内容2内容2内容2内容2内容2',      '内容3内容3内容3内容3内容3内容3内容3内容3内容3内容3',      '内容4内容4内容4内容4内容4内容4内容4内容4内容4内容4',      '内容5内容5内容5内容5内容5内容5内容5内容5内容5内容5',      '内容6内容6内容6内容6内容6内容6内容6内容6内容6内容6',      '内容7内容7内容7内容7内容7内容7内容7内容7内容7内容7'    ]  }]var config = {  theme: 'LIGHT', // LIGHT | DARK  colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],  type: 'x', // 时序图的方向：x为横向，y为纵向  split: 10}var container =  document.getElementById('container')var chart = echarts.getInstanceByDom(container) || echarts.init(container, null, { renderer: 'svg' })var rowNum = data[0].data.lengthvar colNum = data.lengthvar seriesList = [];var axisList = [];for (let i = 0; i < rowNum; i++) {  axisList.push(data[0].data[i])  seriesList.push({    time: data[0].data[i],    place: data[1].data[i],    title: data[2].data[i],    content: data[3].data[i],    value: 0,    label: {      show: true,      lineHeight: 20,      align: config.type === 'x' ? 'left' : (i % 2 == 0 ? 'left' : 'right'),      padding: config.type === 'x' ? [0, 0, 0, -30] : [0, 20, 0, 20],//下 右 上 左      position: config.type === 'x' ? (i % 2 == 0 ? 'top' : 'bottom') : (i % 2 == 0 ? 'left' : 'right'),      formatter: function (params) {        let res = [params.data.content]        if (config.split > 0) {          res = []          for (let j = 0; j < params.data.content.length; j += config.split) {            res.push(params.data.content.slice(j, j + config.split));          }        }        return `{bolder|${params.data.time}    ${params.data.place}\n${params.data.title}}\n{font|${res.join('\n')}}`;      },      rich: {        bolder: {          fontWeight: 700,          color: config.colors[0]        },        font: {          color: config.theme === 'DARK' ? '#B4C2D6' : '#617282'        }      },    },  });}var option = {  grid: {    top: config.type === 'x' ? '50%' : '1%',    left: config.type === 'x' ? '1%' : '50%',    right: config.type === 'x' ? '1%' : '50%',    bottom: config.type === 'x' ? '50%' : '1%'  },  yAxis: {},  xAxis: {},  series: [    {      type: 'line',      symbolSize: 8,      itemStyle: {        color: config.colors[0],        borderColor: config.colors[0],        borderWidth: 2      },      emphasis: { disabled: true },      data: [{}, ...seriesList, {}]    },  ],};var axis = {  type: 'category',  axisLine: { lineStyle: { width: 4, color: config.colors[0] } },  axisTick: { show: false },  splitLine: { show: false },  axisLabel: { show: false },  data: ['', ...axisList, ''],}option.xAxis = config.type === 'x' ? axis : { show: false }option.yAxis = config.type === 'x' ? { show: false } : axischart.setOption(option);window.addEventListener('resize', () => {  chart.resize();});</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      
      <comments>https://blog.eurkon.com/post/b9e0457b.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Butterfly 标签云增加文章数上下标</title>
      <link>https://blog.eurkon.com/post/6687849c.html</link>
      <guid>https://blog.eurkon.com/post/6687849c.html</guid>
      <pubDate>Thu, 15 Jul 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文教程主要针对 Hexo Butterfly 主题博客，在 Butterfly 主题中，&lt;strong&gt;文章标签页&lt;/strong&gt;和&lt;s</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文教程主要针对 Hexo Butterfly 主题博客，在 Butterfly 主题中，<strong>文章标签页</strong>和<strong>标签侧边栏</strong>都有文章标签的词云图，但仅仅用字体大小表示某个标签下的文章数量是不明显的，可以在这个基础上加上表示某个标签下文章数的上下标，其中 <code>&lt;sup&gt;</code> 表示上标，<code>&lt;sub&gt;</code> 表示下标。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/cover/butterfly_tags_cloud.png" alt="Butterfly 标签云增加文章数上下标"></p><h2 id="修改-page-js"><a href="#修改-page-js" class="headerlink" title="修改 page.js"></a>修改 page.js</h2><ol><li><p>打开 <code>\themes\butterfly\layout\includes\page\tags.pug</code> 文件和 <code>\themes\butterfly\layout\includes\widget\card_tags.pug</code> 文件，发现绘制彩色标签云都是使用了 <code>cloudTags</code> 函数。</p><p>另外一个绘制标签云的 <code>tagcloud</code> 函数是 hexo 自带的，有兴趣的可以到 <code>\node_modules\hexo\lib\plugins\helper\tagcloud.js</code> 研究，这里不多介绍。</p></li><li><p>搜索 <code>cloudTags</code> 函数，可以在 <code>\themes\butterfly\scripts\helpers\page.js</code> 找到绘制标签云的代码，增加 <code>&lt;sup&gt;$&#123;tag.length&#125;&lt;/sup&gt;</code> 或 <code>&lt;sub&gt;$&#123;tag.length&#125;&lt;/sub&gt;</code> 可绘制表示标签文章数的上下标。</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">hexo.extend.helper.register(&#x27;cloudTags&#x27;, function (options = &#123;&#125;) &#123;</span><br><span class="line">  const theme = hexo.theme.config</span><br><span class="line">  const env = this</span><br><span class="line">  let source = options.source</span><br><span class="line">  const minfontsize = options.minfontsize</span><br><span class="line">  const maxfontsize = options.maxfontsize</span><br><span class="line">  const limit = options.limit</span><br><span class="line">  const unit = options.unit || &#x27;px&#x27;</span><br><span class="line"></span><br><span class="line">  let result = &#x27;&#x27;</span><br><span class="line">  if (limit &gt; 0) &#123;</span><br><span class="line">    source = source.limit(limit)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  const sizes = []</span><br><span class="line">  source.sort(&#x27;length&#x27;).forEach(tag =&gt; &#123;</span><br><span class="line">    const &#123; length &#125; = tag</span><br><span class="line">    if (sizes.includes(length)) return</span><br><span class="line">    sizes.push(length)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">  const length = sizes.length - 1</span><br><span class="line">  source.forEach(tag =&gt; &#123;</span><br><span class="line">    const ratio = length ? sizes.indexOf(tag.length) / length : 0</span><br><span class="line">    const size = minfontsize + ((maxfontsize - minfontsize) * ratio)</span><br><span class="line">    let style = `font-size: $&#123;parseFloat(size.toFixed(2))&#125;$&#123;unit&#125;;`</span><br><span class="line">    const color = &#x27;rgb(&#x27; + Math.floor(Math.random() * 201) + &#x27;, &#x27; + Math.floor(Math.random() * 201) + &#x27;, &#x27; + Math.floor(Math.random() * 201) + &#x27;)&#x27; // 0,0,0 -&gt; 200,200,200</span><br><span class="line">    style += ` color: $&#123;color&#125;`</span><br><span class="line"><span class="deletion">-   result += `&lt;a href=&quot;$&#123;env.url_for(tag.path)&#125;&quot; style=&quot;$&#123;style&#125;&quot;&gt;$&#123;tag.name&#125;&lt;/a&gt;`</span></span><br><span class="line"><span class="addition">+   result += `&lt;a href=&quot;$&#123;env.url_for(tag.path)&#125;&quot; style=&quot;$&#123;style&#125;&quot;&gt;$&#123;tag.name&#125;&lt;sup&gt;$&#123;tag.length&#125;&lt;/sup&gt;&lt;/a&gt;`</span></span><br><span class="line">  &#125;)</span><br><span class="line">  return result</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p></li></ol><h2 id="Hexo-三连"><a href="#Hexo-三连" class="headerlink" title="Hexo 三连"></a>Hexo 三连</h2><p>执行 Hexo 三连</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo g &amp;&amp; hexo s</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.eurkon.com/tags/Butterfly/">Butterfly</category>
      
      
      <comments>https://blog.eurkon.com/post/6687849c.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Elasticsearch 面试题解析</title>
      <link>https://blog.eurkon.com/post/fae3ec89.html</link>
      <guid>https://blog.eurkon.com/post/fae3ec89.html</guid>
      <pubDate>Fri, 25 Jun 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;什么是-Elasticsearch？&quot;&gt;&lt;a href=&quot;#什么是-Elasticsearch？&quot; class=&quot;headerlink&quot; title=&quot;什么是 Elasticsearch？&quot;&gt;&lt;/a&gt;什么是 Elasticsearch？&lt;/h2&gt;&lt;p&gt;Elasti</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="什么是-Elasticsearch？"><a href="#什么是-Elasticsearch？" class="headerlink" title="什么是 Elasticsearch？"></a>什么是 Elasticsearch？</h2><p>Elasticsearch 是一个分布式的 RESTful 风格的搜索和数据分析引擎。</p><ul><li>查询：Elasticsearch 允许执行和合并多种类型（结构化、非结构化、地理位置、度量指标）的搜索，搜索方式随心而变。</li><li>分析：找到与查询最匹配的十个文档是一回事。但是如果面对的是十亿行日志，又该如何解读呢？Elasticsearch 聚合让您能够从大处着眼，探索数据的趋势和模式。</li><li>速度：Elasticsearch 很快。真的，真的很快。</li><li>可扩展性：可以在笔记本电脑上运行。也可以在承载了 PB 级数据的成百上千台服务器上运行。</li><li>弹性：Elasticsearch 运行在一个分布式的环境中，从设计之初就考虑到了这一点。</li><li>灵活性：具备多个案例场景。数字、文本、地理位置、结构化、非结构化。所有的数据类型都欢迎。</li></ul><h2 id="在-ElasticSearch-中，集群（Cluster），节点（Node），分片（Shard），Indices（索引），replicas（备份）之间是什么关系？"><a href="#在-ElasticSearch-中，集群（Cluster），节点（Node），分片（Shard），Indices（索引），replicas（备份）之间是什么关系？" class="headerlink" title="在 ElasticSearch 中，集群（Cluster），节点（Node），分片（Shard），Indices（索引），replicas（备份）之间是什么关系？"></a>在 ElasticSearch 中，集群（Cluster），节点（Node），分片（Shard），Indices（索引），replicas（备份）之间是什么关系？</h2><ul><li><p>Cluster：集群，一个 ES 集群由一个或多个节点（Node）组成，每个集群都有一个 cluster name 作为标识。</p></li><li><p>node：节点，一个 ES 实例就是一个 node，一个机器可以有多个实例，所以并不能说一台机器就是一个 node，大多数情况下每个 node 运行在一个独立的环境或虚拟机上。</p></li><li><p>index：索引，即一系列 documents 的集合。</p></li><li><p>shard：分片，ES 是分布式搜索引擎，每个索引有一个或多个分片，索引的数据被分配到各个分片上，相当于一桶水用了 N 个杯子装。</p><ul><li>分片有助于横向扩展，N 个分片会被尽可能平均地（rebalance）分配在不同的节点上（例如你有 2 个节点，4 个主分片（不考虑备份），那么每个节点会分到 2 个分片，后来你增加了 2 个节点，那么你这 4 个节点上都会有 1 个分片，这个过程叫 relocation，ES 感知后自动完成）。</li><li>分片是独立的，对于一个 Search Request 的行为，每个分片都会执行这个 Request。</li><li>每个分片都是一个 Lucene Index，所以一个分片只能存放 <code>Integer.MAX_VALUE - 128 = 2,147,483,519</code> 个 docs。[LUCENE-5843] IndexWriter should refuse to create an index with more than INT_MAX docs</li></ul></li><li><p>replica：复制，可以理解为备份分片，相应地有 primary shard（主分片）。</p><ul><li>主分片和备分片不会出现在同一个节点上（防止单点故障），默认情况下一个索引创建 5 个分片一个备份（即 5 primary + 5 replica = 10 个分片）</li><li>如果你只有一个节点，那么 5 个 replica 都无法分配（unassigned），此时 cluster status 会变成 Yellow。</li><li>replica 的作用主要包括:<ul><li>容灾：primary 分片丢失，replica 分片就会被顶上去成为新的主分片，同时根据这个新的主分片创建新的 replica，集群数据安然无恙</li><li>提高查询性能：replica 和 primary 分片的数据是相同的，所以对于一个 query 既可以查主分片也可以查备分片，在合适的范围内多个 replica 性能会更优（但要考虑资源占用也会提升 <code>cpu</code>/<code>disk</code>/<code>heap</code>），另外 index request 只能发生在主分片上，replica 不能执行 index request。</li></ul></li><li>对于一个索引，除非重建索引否则不能调整分片的数目（主分片数，number_of_shards），但可以随时调整 replica 数（number_of_replicas）。</li></ul></li></ul><h2 id="Elasticsearch-了解多少，说说你们公司-es-的集群架构，索引数据大小，分片有多少，以及一些调优手段。"><a href="#Elasticsearch-了解多少，说说你们公司-es-的集群架构，索引数据大小，分片有多少，以及一些调优手段。" class="headerlink" title="Elasticsearch 了解多少，说说你们公司 es 的集群架构，索引数据大小，分片有多少，以及一些调优手段。"></a>Elasticsearch 了解多少，说说你们公司 es 的集群架构，索引数据大小，分片有多少，以及一些调优手段。</h2><p><strong>面试官：</strong>想了解应聘者之前公司接触的 ES 使用场景、规模，有没有做过比较大规模的索引设计、规划、调优。</p><p><strong>解答：</strong>如实结合自己的实践场景回答即可。</p><p>比如：ES 集群架构 13 个节点，索引根据通道不同共 20+ 索引，根据日期，每日递增 20+，索引：10分片，每日递增 1亿+ 数据，每个通道每天索引大小控制：150GB 之内。仅索引层面调优手段：</p><ol><li><p>设计阶段调优</p><ul><li>根据业务增量需求，采取基于日期模板创建索引，通过 roll over API 滚动索引；</li><li>使用别名进行索引管理；</li><li>每天凌晨定时对索引做 force_merge 操作，以释放空间；</li><li>采取冷热分离机制，热数据存储到 SSD，提高检索效率；冷数据定期进行 shrink 操作，以缩减存储；</li><li>采取 curator 进行索引的生命周期管理；</li><li>仅针对需要分词的字段，合理的设置分词器；</li><li>Mapping 阶段充分结合各个字段的属性，是否需要检索、是否需要存储等。</li></ul></li><li><p>写入调优</p><ul><li>写入前副本数设置为 0；</li><li>写入前关闭 refresh_interval 设置为 -1，禁用刷新机制；</li><li>写入过程中：采取 bulk 批量写入；</li><li>写入后恢复副本数和刷新间隔；</li><li>尽量使用自动生成的 id。</li></ul></li><li><p>查询调优</p><ul><li>禁用 wildcard；</li><li>禁用批量 terms（成百上千的场景）；</li><li>充分利用倒排索引机制，能 keyword 类型尽量 keyword；</li><li>数据量大时候，可以先基于时间敲定索引再检索；</li><li>设置合理的路由机制。</li></ul></li><li><p>其他调优</p><ul><li>部署调优，业务调优等。</li></ul></li></ol><p>上面的提及一部分，面试者就基本对你之前的实践或者运维经验有所评估了。</p><h2 id="ES-写数据过程"><a href="#ES-写数据过程" class="headerlink" title="ES 写数据过程"></a>ES 写数据过程</h2><ol><li>客户端选择一个 node 发送请求过去，这个 node 就是 coordinating node（协调节点）。</li><li>coordinating node 对 document 进行路由，将请求转发给对应的 node（有 primary shard）。</li><li>实际的 node 上的 primary shard 处理请求，然后将数据同步到 replica node。</li><li>coordinating node 如果发现 primary node 和所有 replica node 都搞定之后，就返回响应结果给客户端。</li></ol><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/es_write.jpg" alt="ES 写数据"></p><h2 id="ES-读数据过程"><a href="#ES-读数据过程" class="headerlink" title="ES 读数据过程"></a>ES 读数据过程</h2><p>可以通过 document id 来查询，会根据 document id 进行 hash，判断出来当时把 document id 分配到了哪个 shard 上面去，从那个 shard 去查询。</p><ol><li>客户端发送请求到任意一个 node，成为 coordinate node。</li><li>coordinate node 对 document id 进行哈希路由，将请求转发到对应的 node，此时会使用 round-robin 随机轮询算法，在 primary shard 以及其所有 replica 中随机选择一个，让读请求负载均衡。</li><li>接收请求的 node 返回 document 给 coordinate node。</li><li>coordinate node 返回 document 给客户端。</li></ol><h2 id="索引数据多了怎么办，如何调优，部署？"><a href="#索引数据多了怎么办，如何调优，部署？" class="headerlink" title="索引数据多了怎么办，如何调优，部署？"></a>索引数据多了怎么办，如何调优，部署？</h2><p>索引数据的规划，应在前期做好规划，正所谓“设计先行，编码在后”，这样才能有效的避免突如其来的数据激增导致集群处理能力不足引发的线上客户检索或者其他业务受到影响。</p><ol><li><p>动态索引层面</p><p>基于模板 + 时间 + rollover api 滚动创建索引，举例：设计阶段定义：blog 索引的模板格式为：<code>blog_index_时间戳</code> 的形式，每天递增数据。这样做的好处：不至于数据量激增导致单个索引数据量非常大，接近于上线 2 的 32 次幂 - 1，索引存储达到了 TB+ 甚至更大。</p><p>一旦单个索引很大，存储等各种风险也随之而来，所以要提前考虑，及早避免。</p></li><li><p>存储层面</p><p>冷热数据分离存储，热数据（比如最近 3 天或者一周的数据），其余为冷数据。</p><p>对于冷数据不会再写入新数据，可以考虑定期 force_merge 加 shrink 压缩操作，节省存储空间和检索效率。</p></li><li><p>部署层面</p><p>一旦之前没有规划，这里就属于应急策略。</p><p>结合 ES 自身的支持动态扩展的特点，动态新增机器的方式可以缓解集群压力，注意：如果之前主节点等规划合理，不需要重启集群也能完成动态新增的。</p></li></ol><h2 id="详细描述一下-Elasticsearch-搜索的过程？"><a href="#详细描述一下-Elasticsearch-搜索的过程？" class="headerlink" title="详细描述一下 Elasticsearch 搜索的过程？"></a>详细描述一下 Elasticsearch 搜索的过程？</h2><p>搜索拆解为 Query Then Fetch 两个阶段。</p><ol><li><p>query 阶段的目的：定位到位置，但不取。步骤拆解如下：</p><ol><li>假设一个索引数据有 5 主 + 1 副本，共 10 分片，一次请求会命中（主或者副本分片中）的一个。</li><li>每个分片在本地进行查询，结果返回到本地有序的优先队列中。</li><li>第 2 步骤的结果发送到协调节点，协调节点产生一个全局的排序列表。</li></ol></li><li><p>fetch 阶段的目的：取数据。</p><ol><li>路由节点获取所有文档，返回给客户端。</li></ol></li></ol><h2 id="详细描述一下-Elasticsearch-更新和删除文档的过程。"><a href="#详细描述一下-Elasticsearch-更新和删除文档的过程。" class="headerlink" title="详细描述一下 Elasticsearch 更新和删除文档的过程。"></a>详细描述一下 Elasticsearch 更新和删除文档的过程。</h2><p>删除和更新也都是写操作，但是 Elasticsearch 中的文档是不可变的，因此不能被删除或者改动以展示其变更；</p><p>磁盘上的每个段都有一个相应的 .del 文件。当删除请求发送后，文档并没有真的被删除，而是在 .del 文件中被标记为删除。该文档依然能匹配查询，但是会在结果中被过滤掉。当段合并时，在 .del 文件中被标记为删除的文档将不会被写入新段。</p><p>在新的文档被创建时，Elasticsearch 会为该文档指定一个版本号，当执行更新时，旧版本的文档在 .del 文件中被标记为删除，新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询，但是会在结果中被过滤掉。</p><h2 id="Elasticsearch-是如何实现-Master-选举的？"><a href="#Elasticsearch-是如何实现-Master-选举的？" class="headerlink" title="Elasticsearch 是如何实现 Master 选举的？"></a>Elasticsearch 是如何实现 Master 选举的？</h2><ul><li>Elasticsearch 的选主是 ZenDiscovery 模块负责的，主要包含 Ping（节点之间通过这个 RPC 来发现彼此）和 Unicast（单播模块包含一个主机列表以控制哪些节点需要 ping 通）这两部分；</li><li>对所有可以成为 master 的节点（node.master: true）根据 nodeId 字典排序，每次选举每个节点都把自己所知道的节点排一次序，然后选出第一个（第 0 位）节点，暂且认为它是 master 节点。</li><li>如果对某个节点的投票数达到一定的值（可以成为 master 节点数 n/2+1）并且该节点自己也选举自己，那这个节点就是 master。否则重新选举一直到满足上述条件。</li><li>补充：master 节点的职责主要包括集群、节点和索引的管理，不负责文档级别的管理；data 节点可以关闭 http 功能。</li></ul><h2 id="Elasticsearch-中的节点（比如共-20-个），其中的-10-个选了一个-master，另外-10-个选了另一个-master，怎么办？"><a href="#Elasticsearch-中的节点（比如共-20-个），其中的-10-个选了一个-master，另外-10-个选了另一个-master，怎么办？" class="headerlink" title="Elasticsearch 中的节点（比如共 20 个），其中的 10 个选了一个 master，另外 10 个选了另一个 master，怎么办？"></a>Elasticsearch 中的节点（比如共 20 个），其中的 10 个选了一个 master，另外 10 个选了另一个 master，怎么办？</h2><ul><li>当集群 master 候选数量不小于 3 个时，可以通过设置最少投票通过数量（discovery.zen.minimum_master_nodes）超过所有候选节点一半以上来解决脑裂问题；</li><li>当候选数量为两个时，只能修改为唯一的一个 master 候选，其他作为 data 节点，避免脑裂问题。</li></ul><h2 id="在并发情况下，Elasticsearch-如果保证读写一致？"><a href="#在并发情况下，Elasticsearch-如果保证读写一致？" class="headerlink" title="在并发情况下，Elasticsearch 如果保证读写一致？"></a>在并发情况下，Elasticsearch 如果保证读写一致？</h2><p>可以通过版本号使用乐观并发控制，以确保新版本不会被旧版本覆盖，由应用层来处理具体的冲突；</p><p>另外对于写操作，一致性级别支持 <code>quorum</code> / <code>one</code> / <code>all</code>，默认为 <code>quorum</code>，即只有当大多数分片可用时才允许写操作。但即使大多数可用，也可能存在因为网络等原因导致写入副本失败，这样该副本被认为故障，分片将会在一个不同的节点上重建。</p><p>对于读操作，可以设置 replication 为 sync（默认），这使得操作在主分片和副本分片都完成后才会返回；如果设置 replication 为 async 时，也可以通过设置搜索请求参数 _preference 为 primary 来查询主分片，确保文档是最新版本。</p><h2 id="ES-集群的三种状态？"><a href="#ES-集群的三种状态？" class="headerlink" title="ES 集群的三种状态？"></a>ES 集群的三种状态？</h2><ul><li>Green：所有主分片和备份分片都准备就绪（分配成功），即使有一台机器挂了（假设一台机器一个实例），数据都不会丢失，但会变成 Yellow 状态；</li><li>Yellow：所有主分片准备就绪，但存在至少一个主分片（假设是 A）对应的备份分片没有就绪，此时集群属于警告状态，意味着集群高可用和容灾能力下降，如果刚好 A 所在的机器挂了，并且你只设置了一个备份（已处于未就绪状态），那么 A 的数据就会丢失（查询结果不完整），此时集群进入 Red 状态；</li><li>Red：至少有一个主分片没有就绪（直接原因是找不到对应的备份分片成为新的主分片），此时查询的结果会出现数据丢失（不完整）。</li></ul><h2 id="ES-Filter-DSL？"><a href="#ES-Filter-DSL？" class="headerlink" title="ES Filter DSL？"></a>ES Filter DSL？</h2><ul><li><p>term 过滤：主要用于精确匹配哪些值，比如数字，日期，布尔值或 not_analyzed 的字符串（未经分析的文本数据类型）。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">&quot;term&quot;</span>: &#123; <span class="attr">&quot;age&quot;</span>:    <span class="number">26</span>           &#125;&#125;</span><br><span class="line">&#123; <span class="attr">&quot;term&quot;</span>: &#123; <span class="attr">&quot;date&quot;</span>:   <span class="string">&quot;2014-09-01&quot;</span> &#125;&#125;</span><br><span class="line">&#123; <span class="attr">&quot;term&quot;</span>: &#123; <span class="attr">&quot;public&quot;</span>: <span class="literal">true</span>         &#125;&#125;</span><br><span class="line">&#123; <span class="attr">&quot;term&quot;</span>: &#123; <span class="attr">&quot;tag&quot;</span>:    <span class="string">&quot;full_text&quot;</span>  &#125;&#125;</span><br></pre></td></tr></table></figure></p></li><li><p>terms 过滤：terms 跟 term 有点类似，但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值，那么文档需要一起去做匹配。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">&quot;term&quot;</span>: &#123; <span class="attr">&quot;tag&quot;</span>: [ <span class="string">&quot;tag1&quot;</span>, <span class="string">&quot;tag2&quot;</span>, <span class="string">&quot;tag3&quot;</span> ] &#125;&#125;</span><br></pre></td></tr></table></figure></p></li><li><p>range 过滤：range过滤允许我们按照指定范围查找一批数据：</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">&quot;range&quot;</span>: &#123; <span class="attr">&quot;age&quot;</span>: &#123; <span class="attr">&quot;gte&quot;</span>: <span class="number">20</span>, <span class="attr">&quot;lt&quot;</span>: <span class="number">30</span> &#125; &#125; &#125;</span><br></pre></td></tr></table></figure></p><ul><li>gt：大于</li><li>gte：大于等于</li><li>lt：小于</li><li>lte：小于等于</li></ul></li><li><p>exists 和 missing 过滤：exists 和 missing 过滤可以用于查找文档中是否包含指定字段或没有某个字段，类似于 SQL 语句中的 IS_NULL 条件。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">&quot;exists&quot;</span>: &#123; <span class="attr">&quot;field&quot;</span>: <span class="string">&quot;title&quot;</span> &#125; &#125;</span><br></pre></td></tr></table></figure></p></li><li><p>bool 过滤：可以用来合并多个过滤条件查询结果的布尔逻辑，它包含以下操作符，这些参数可以分别继承一个过滤条件或者一个过滤条件的数组。</p><ul><li>must：多个查询条件的完全匹配,相当于 and。</li><li>must_not：多个查询条件的相反匹配，相当于 not。</li><li>should：至少有一个查询条件匹配, 相当于 or。<p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">&#123; </span><br><span class="line">    <span class="attr">&quot;bool&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;must&quot;</span>:     &#123; <span class="attr">&quot;term&quot;</span>: &#123; <span class="attr">&quot;folder&quot;</span>: <span class="string">&quot;inbox&quot;</span> &#125;&#125;,</span><br><span class="line">        <span class="attr">&quot;must_not&quot;</span>: &#123; <span class="attr">&quot;term&quot;</span>: &#123; <span class="attr">&quot;tag&quot;</span>:    <span class="string">&quot;spam&quot;</span>  &#125;&#125;,</span><br><span class="line">        <span class="attr">&quot;should&quot;</span>: [ </span><br><span class="line">                    &#123; <span class="attr">&quot;term&quot;</span>: &#123; <span class="attr">&quot;starred&quot;</span>: <span class="literal">true</span>   &#125;&#125;,</span><br><span class="line">                    &#123; <span class="attr">&quot;term&quot;</span>: &#123; <span class="attr">&quot;unread&quot;</span>:  <span class="literal">true</span>   &#125;&#125;</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li></ul></li></ul><h2 id="ES-Query-DSL？"><a href="#ES-Query-DSL？" class="headerlink" title="ES Query DSL？"></a>ES Query DSL？</h2><ul><li><p>match_all 查询：可以查询到所有文档，是没有查询条件下的默认语句。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">&quot;match_all&quot;</span>: &#123;&#125; &#125;</span><br></pre></td></tr></table></figure></p></li><li><p>match 查询：是一个标准查询，不管你需要全文本查询还是精确查询基本上都要用到它。如果你使用 match 查询一个全文本字段，它会在真正查询之前用分析器先分析 match 一下查询字符：</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">&quot;match&quot;</span>: &#123; <span class="attr">&quot;tweet&quot;</span>: <span class="string">&quot;About Search&quot;</span> &#125; &#125;</span><br></pre></td></tr></table></figure></p><p>如果用 match 下指定了一个确切值，在遇到数字，日期，布尔值或者 not_analyzed 的字符串时，它将为你搜索你给定的值：</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">&quot;match&quot;</span>: &#123; <span class="attr">&quot;age&quot;</span>:    <span class="number">26</span>           &#125;&#125;</span><br><span class="line">&#123; <span class="attr">&quot;match&quot;</span>: &#123; <span class="attr">&quot;date&quot;</span>:   <span class="string">&quot;2014-09-01&quot;</span> &#125;&#125;</span><br><span class="line">&#123; <span class="attr">&quot;match&quot;</span>: &#123; <span class="attr">&quot;public&quot;</span>: <span class="literal">true</span>         &#125;&#125;</span><br><span class="line">&#123; <span class="attr">&quot;match&quot;</span>: &#123; <span class="attr">&quot;tag&quot;</span>:    <span class="string">&quot;full_text&quot;</span>  &#125;&#125;</span><br></pre></td></tr></table></figure></p><p>提示：做精确匹配搜索时，你最好用过滤语句，因为过滤语句可以缓存数据。</p><p>match 查询只能就指定某个确切字段某个确切的值进行搜索，而你要做的就是为它指定正确的字段名以避免语法错误。</p></li><li><p>multi_match 查询：允许你做 match 查询的基础上同时搜索多个字段，在多个字段中同时查一个：</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;multi_match&quot;</span>: &#123; </span><br><span class="line">        <span class="attr">&quot;query&quot;</span>:    <span class="string">&quot;full text search&quot;</span>,</span><br><span class="line">        <span class="attr">&quot;fields&quot;</span>:   [ <span class="string">&quot;title&quot;</span>, <span class="string">&quot;body&quot;</span> ]</span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li><li><p>bool 查询：与 bool 过滤相似，用于合并多个查询子句。不同的是，bool 过滤可以直接给出是否匹配成功，而 bool 查询要计算每一个查询子句的 _score （相关性分值）。</p><ul><li>must：查询指定文档一定要被包含。</li><li>must_not：查询指定文档一定不要被包含。</li><li>should：查询指定文档，有则可以为文档相关性加分。</li></ul><p>以下查询将会找到 title 字段中包含 &quot;how to make millions&quot;，并且 &quot;tag&quot; 字段没有被标为 spam。 如果有标识为 &quot;starred&quot; 或者发布日期为 2014 年之前，那么这些匹配的文档将比同类网站等级高：</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">&#123; </span><br><span class="line">    <span class="attr">&quot;bool&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;must&quot;</span>:     &#123; <span class="attr">&quot;match&quot;</span>: &#123; <span class="attr">&quot;title&quot;</span>: <span class="string">&quot;how to make millions&quot;</span> &#125;&#125;,</span><br><span class="line">        <span class="attr">&quot;must_not&quot;</span>: &#123; <span class="attr">&quot;match&quot;</span>: &#123; <span class="attr">&quot;tag&quot;</span>:   <span class="string">&quot;spam&quot;</span> &#125;&#125;,</span><br><span class="line">        <span class="attr">&quot;should&quot;</span>: [</span><br><span class="line">            &#123; <span class="attr">&quot;match&quot;</span>: &#123; <span class="attr">&quot;tag&quot;</span>: <span class="string">&quot;starred&quot;</span> &#125;&#125;,</span><br><span class="line">            &#123; <span class="attr">&quot;range&quot;</span>: &#123; <span class="attr">&quot;date&quot;</span>: &#123; <span class="attr">&quot;gte&quot;</span>: <span class="string">&quot;2014-01-01&quot;</span> &#125;&#125;&#125;</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>提示： 如果 bool 查询下没有 must 子句，那至少应该有一个 should 子句。但是如果有 must 子句，那么没有 should 子句也可以进行查询。</p></li><li><p>wildcards 查询：允许使用通配符 <code>*</code>（代表 0 个或多个字符）和 <code>?</code>（代表任意 1 个字符）来进行查询。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">&quot;query&quot;</span>: &#123; <span class="attr">&quot;wildcard&quot;</span>: &#123; <span class="attr">&quot;postcode&quot;</span>: <span class="string">&quot;W?F*HW&quot;</span> &#125; &#125; &#125;</span><br></pre></td></tr></table></figure></p></li><li><p>regexp 查询：使用标准的 shell 通配符查询。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">&quot;query&quot;</span>: &#123; <span class="attr">&quot;regexp&quot;</span>: &#123; <span class="attr">&quot;postcode&quot;</span>: <span class="string">&quot;W[0-9].+&quot;</span> &#125; &#125; &#125;</span><br></pre></td></tr></table></figure></p></li><li><p>prefix 查询：以什么字符开头的，可以更简单地用 prefix。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">&quot;query&quot;</span>: &#123; <span class="attr">&quot;prefix&quot;</span>: &#123; <span class="attr">&quot;hostname&quot;</span>: <span class="string">&quot;wxopen&quot;</span> &#125; &#125; &#125;</span><br></pre></td></tr></table></figure></p></li><li><p>match_phrase 查询：当你需要寻找邻近的几个单词时，你会使用 match_phrase 查询。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;query&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;match_phrase&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;title&quot;</span>: <span class="string">&quot;quick brown fox&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>和 match 查询类似，match_phrase 查询首先解析查询字符串来产生一个词条列表。然后会搜索所有的词条，但只保留含有了所有搜索词条的文档，并且词条的位置要邻接。一个针对短语 quick fox 的查询不会匹配，我们的任何文档，因为没有文档含有邻接在一起的 quick 和 box 词条。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;match&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;title&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;query&quot;</span>: <span class="string">&quot;quick brown fox&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;type&quot;</span>:  <span class="string">&quot;phrase&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li><li><p>query_string查询：允许我们在单个查询字符串中指定 <code>AND</code> | <code>OR</code> | <code>NOT</code> 条件，同时也和 multi_match 一样，支持多字段搜索。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;query&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;query_string&quot;</span> : &#123;</span><br><span class="line">            <span class="attr">&quot;fields&quot;</span> : [<span class="string">&quot;title&quot;</span>],</span><br><span class="line">            <span class="attr">&quot;query&quot;</span> : <span class="string">&quot;系统学 AND es&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li></ul><h2 id="keyword-和-text-类型的查询匹配规则"><a href="#keyword-和-text-类型的查询匹配规则" class="headerlink" title="keyword 和 text 类型的查询匹配规则"></a>keyword 和 text 类型的查询匹配规则</h2><div class="table-container"><table><thead><tr><th>关键词</th><th>keyword</th><th>text</th><th>支持分词</th></tr></thead><tbody><tr><td>term</td><td>完全匹配</td><td>查询条件必须都是 text 分词中的，且不能多余，<br>多个分词时必须连续，顺序不能颠倒。</td><td>否</td></tr><tr><td>match</td><td>完全匹配</td><td>match 分词结果和 text 的分词结果有相同的即可，<br>不考虑顺序</td><td>是</td></tr><tr><td>match_phrase</td><td>完全匹配</td><td>match_phrase 的分词结果必须在 text 字段分词中都包含，<br>而且顺序必须相同，而且必须都是连续的。</td><td>是</td></tr><tr><td>query_string</td><td>完全匹配</td><td>query_string 中的分词结果至少有一个在 text 字段的<br>分词结果中，不考虑顺序</td><td>是</td></tr></tbody></table></div>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Elasticsearch/">Elasticsearch</category>
      
      
      <comments>https://blog.eurkon.com/post/fae3ec89.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Emoji 表情大全</title>
      <link>https://blog.eurkon.com/post/f349e1f0.html</link>
      <guid>https://blog.eurkon.com/post/f349e1f0.html</guid>
      <pubDate>Tue, 22 Jun 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;表情与情感&quot;&gt;&lt;a href=&quot;#表情与情感&quot; class=&quot;headerlink&quot; title=&quot;表情与情感&quot;&gt;&lt;/a&gt;表情与情感&lt;/h2&gt;&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;表情&lt;/th</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="表情与情感"><a href="#表情与情感" class="headerlink" title="表情与情感"></a>表情与情感</h2><div class="table-container"><table><thead><tr><th>表情</th><th>名称</th><th>表情</th><th>名称</th><th>表情</th><th>名称</th></tr></thead><tbody><tr><td>😀</td><td>嘿嘿</td><td>😃</td><td>哈哈</td><td>😄</td><td>大笑</td></tr><tr><td>😁</td><td>嘻嘻</td><td>😆</td><td>斜眼笑</td><td>😅</td><td>苦笑</td></tr><tr><td>🤣</td><td>笑得满地打滚</td><td>😂</td><td>笑哭了</td><td>🙂</td><td>呵呵</td></tr><tr><td>🙃</td><td>倒脸</td><td>😉</td><td>眨眼</td><td>😊</td><td>羞涩微笑</td></tr><tr><td>😇</td><td>微笑天使</td><td>🥰</td><td>喜笑颜开</td><td>😍</td><td>花痴</td></tr><tr><td>🤩</td><td>好崇拜哦</td><td>😘</td><td>飞吻</td><td>😗</td><td>亲亲</td></tr><tr><td>☺️</td><td>微笑</td><td>😚</td><td>羞涩亲亲</td><td>😙</td><td>微笑亲亲</td></tr><tr><td>😋</td><td>好吃</td><td>😛</td><td>吐舌</td><td>😜</td><td>单眼吐舌</td></tr><tr><td>🤪</td><td>滑稽</td><td>😝</td><td>眯眼吐舌</td><td>🤑</td><td>发财</td></tr><tr><td>🤗</td><td>抱抱</td><td>🤭</td><td>不说</td><td>🤫</td><td>安静的脸</td></tr><tr><td>🤔</td><td>想一想</td><td>🤐</td><td>闭嘴</td><td>🤨</td><td>挑眉</td></tr><tr><td>😐</td><td>冷漠</td><td>😑</td><td>无语</td><td>😶</td><td>沉默</td></tr><tr><td>😏</td><td>得意</td><td>😒</td><td>不高兴</td><td>🙄</td><td>翻白眼</td></tr><tr><td>😬</td><td>龇牙咧嘴</td><td>🤥</td><td>说谎</td><td>😌</td><td>松了口气</td></tr><tr><td>😔</td><td>沉思</td><td>😪</td><td>困</td><td>🤤</td><td>流口水</td></tr><tr><td>😴</td><td>睡着了</td><td>😷</td><td>感冒</td><td>🤒</td><td>发烧</td></tr><tr><td>🤕</td><td>受伤</td><td>🤢</td><td>恶心</td><td>🤮</td><td>呕吐</td></tr><tr><td>🤧</td><td>打喷嚏</td><td>🥵</td><td>脸发烧</td><td>🥶</td><td>冷脸</td></tr><tr><td>🥴</td><td>头昏眼花</td><td>😵</td><td>晕头转向</td><td>🤯</td><td>爆炸头</td></tr><tr><td>🤠</td><td>牛仔帽脸</td><td>🥳</td><td>聚会笑脸</td><td>😎</td><td>墨镜笑脸</td></tr><tr><td>🤓</td><td>书呆子脸</td><td>🧐</td><td>带单片眼镜的脸</td><td>😕</td><td>困扰</td></tr><tr><td>😟</td><td>担心</td><td>🙁</td><td>微微不满</td><td>☹️</td><td>不满</td></tr><tr><td>😮</td><td>吃惊</td><td>😯</td><td>缄默</td><td>😲</td><td>震惊</td></tr><tr><td>😳</td><td>脸红</td><td>🥺</td><td>恳求的脸</td><td>😦</td><td>啊</td></tr><tr><td>😧</td><td>极度痛苦</td><td>😨</td><td>害怕</td><td>😰</td><td>冷汗</td></tr><tr><td>😥</td><td>失望但如释重负</td><td>😢</td><td>哭</td><td>😭</td><td>放声大哭</td></tr><tr><td>😱</td><td>吓死了</td><td>😖</td><td>困惑</td><td>😣</td><td>痛苦</td></tr><tr><td>😞</td><td>失望</td><td>😓</td><td>汗</td><td>😩</td><td>累死了</td></tr><tr><td>😫</td><td>累</td><td>🥱</td><td>打呵欠</td><td>😤</td><td>傲慢</td></tr><tr><td>😡</td><td>怒火中烧</td><td>😠</td><td>生气</td><td>🤬</td><td>嘴上有符号的脸</td></tr><tr><td>😈</td><td>恶魔微笑</td><td>👿</td><td>生气的恶魔</td><td>💀</td><td>头骨</td></tr><tr><td>☠️</td><td>骷髅</td><td>💩</td><td>大便</td><td>🤡</td><td>小丑脸</td></tr><tr><td>👹</td><td>食人魔</td><td>👺</td><td>小妖精</td><td>👻</td><td>鬼</td></tr><tr><td>👽</td><td>外星人</td><td>👾</td><td>外星怪物</td><td>🤖</td><td>机器人</td></tr><tr><td>😺</td><td>大笑的猫</td><td>😸</td><td>微笑的猫</td><td>😹</td><td>笑出眼泪的猫</td></tr><tr><td>😻</td><td>花痴的猫</td><td>😼</td><td>奸笑的猫</td><td>😽</td><td>亲亲猫</td></tr><tr><td>🙀</td><td>疲倦的猫</td><td>😿</td><td>哭泣的猫</td><td>😾</td><td>生气的猫</td></tr><tr><td>🙈</td><td>非礼勿视</td><td>🙉</td><td>非礼勿听</td><td>🙊</td><td>非礼勿言</td></tr><tr><td>💋</td><td>唇印</td><td>💌</td><td>情书</td><td>💘</td><td>心中箭了</td></tr><tr><td>💝</td><td>系有缎带的心</td><td>💖</td><td>闪亮的心</td><td>💗</td><td>搏动的心</td></tr><tr><td>💓</td><td>心跳</td><td>💞</td><td>舞动的心</td><td>💕</td><td>两颗心</td></tr><tr><td>💟</td><td>心型装饰</td><td>❣️</td><td>心叹号</td><td>💔</td><td>心碎</td></tr><tr><td>❤️</td><td>红心</td><td>🧡</td><td>橙心</td><td>💛</td><td>黄心</td></tr><tr><td>💚</td><td>绿心</td><td>💙</td><td>蓝心</td><td>💜</td><td>紫心</td></tr><tr><td>🤎</td><td>棕心</td><td>🖤</td><td>黑心</td><td>🤍</td><td>白心</td></tr><tr><td>💯</td><td>一百分</td><td>💢</td><td>怒</td><td>💥</td><td>爆炸</td></tr><tr><td>💫</td><td>头晕</td><td>💦</td><td>汗滴</td><td>💨</td><td>尾气</td></tr><tr><td>🕳️</td><td>洞</td><td>💣</td><td>炸弹</td><td>💬</td><td>话语气泡</td></tr><tr><td>👁️‍🗨️</td><td>眼睛对话框</td><td>🗨️</td><td>朝左的话语气泡</td><td>🗯️</td><td>愤怒话语气泡</td></tr><tr><td>💭</td><td>内心活动气泡</td><td>💤</td><td>睡着</td><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table></div><h2 id="人类和身体"><a href="#人类和身体" class="headerlink" title="人类和身体"></a>人类和身体</h2><div class="table-container"><table><thead><tr><th>表情</th><th>名称</th><th>表情</th><th>名称</th><th>表情</th><th>名称</th></tr></thead><tbody><tr><td>👋</td><td>挥手</td><td>🤚</td><td>立起的手背</td><td>🖐️</td><td>手掌</td></tr><tr><td>✋</td><td>举起手</td><td>🖖</td><td>瓦肯举手礼</td><td>👌</td><td>OK</td></tr><tr><td>🤏</td><td>捏合的手势</td><td>✌️</td><td>胜利手势</td><td>🤞</td><td>交叉的手指</td></tr><tr><td>🤟</td><td>爱你的手势</td><td>🤘</td><td>摇滚</td><td>🤙</td><td>给我打电话</td></tr><tr><td>👈</td><td>反手食指向左指</td><td>👉</td><td>反手食指向右指</td><td>👆</td><td>反手食指向上指</td></tr><tr><td>🖕</td><td>竖中指</td><td>👇</td><td>反手食指向下指</td><td>☝️</td><td>食指向上指</td></tr><tr><td>👍</td><td>拇指向上</td><td>👎</td><td>拇指向下</td><td>✊</td><td>举起拳头</td></tr><tr><td>👊</td><td>出拳</td><td>🤛</td><td>朝左的拳头</td><td>🤜</td><td>朝右的拳头</td></tr><tr><td>👏</td><td>鼓掌</td><td>🙌</td><td>举双手</td><td>👐</td><td>张开双手</td></tr><tr><td>🤲</td><td>掌心向上托起</td><td>🤝</td><td>握手</td><td>🙏</td><td>双手合十</td></tr><tr><td>✍️</td><td>写字</td><td>💅</td><td>涂指甲油</td><td>🤳</td><td>自拍</td></tr><tr><td>💪</td><td>肌肉</td><td>🦾</td><td>机械手臂</td><td>🦿</td><td>机械腿</td></tr><tr><td>🦵</td><td>腿</td><td>🦶</td><td>脚</td><td>👂</td><td>耳朵</td></tr><tr><td>🦻</td><td>戴助听器的耳朵</td><td>👃</td><td>鼻子</td><td>🧠</td><td>脑</td></tr><tr><td>🦷</td><td>牙齿</td><td>🦴</td><td>骨头</td><td>👀</td><td>双眼</td></tr><tr><td>👁️</td><td>眼睛</td><td>👅</td><td>舌头</td><td>👄</td><td>嘴</td></tr><tr><td>👶</td><td>小宝贝</td><td>🧒</td><td>儿童</td><td>👦</td><td>男孩</td></tr><tr><td>👧</td><td>女孩</td><td>🧑</td><td>成人</td><td>👱</td><td>金色头发的人</td></tr><tr><td>👨</td><td>男人</td><td>🧔</td><td>有胡子的人</td><td>👱‍♂️</td><td>金发男</td></tr><tr><td>👨‍🦰</td><td>男人: 红发</td><td>👨‍🦱</td><td>男人: 卷发</td><td>👨‍🦳</td><td>男人: 白发</td></tr><tr><td>👨‍🦲</td><td>男人: 秃顶</td><td>👩</td><td>女人</td><td>👱‍♀️</td><td>金发女</td></tr><tr><td>👩‍🦰</td><td>女人: 红发</td><td>👩‍🦱</td><td>女人: 卷发</td><td>👩‍🦳</td><td>女人: 白发</td></tr><tr><td>👩‍🦲</td><td>女人: 秃顶</td><td>🧓</td><td>老年人</td><td>👴</td><td>老爷爷</td></tr><tr><td>👵</td><td>老奶奶</td><td>🙍</td><td>皱眉</td><td>🙍‍♂️</td><td>皱眉男</td></tr><tr><td>🙍‍♀️</td><td>皱眉女</td><td>🙎</td><td>撅嘴</td><td>🙎‍♂️</td><td>撅嘴男</td></tr><tr><td>🙎‍♀️</td><td>撅嘴女</td><td>🙅</td><td>禁止手势</td><td>🙅‍♂️</td><td>禁止手势男</td></tr><tr><td>🙅‍♀️</td><td>禁止手势女</td><td>🙆</td><td>OK手势</td><td>🙆‍♂️</td><td>OK手势男</td></tr><tr><td>🙆‍♀️</td><td>OK手势女</td><td>💁</td><td>前台</td><td>💁‍♂️</td><td>前台男</td></tr><tr><td>💁‍♀️</td><td>前台女</td><td>🙋</td><td>举手</td><td>🙋‍♂️</td><td>男生举手</td></tr><tr><td>🙋‍♀️</td><td>女生举手</td><td>🧏</td><td>失聪者</td><td>🧏‍♂️</td><td>失聪的男人</td></tr><tr><td>🧏‍♀️</td><td>失聪的女人</td><td>🙇</td><td>鞠躬</td><td>🙇‍♂️</td><td>男生鞠躬</td></tr><tr><td>🙇‍♀️</td><td>女生鞠躬</td><td>🤦</td><td>捂脸</td><td>🤦‍♂️</td><td>男生捂脸</td></tr><tr><td>🤦‍♀️</td><td>女生捂脸</td><td>🤷</td><td>耸肩</td><td>🤷‍♂️</td><td>男生耸肩</td></tr><tr><td>🤷‍♀️</td><td>女生耸肩</td><td>👨‍⚕️</td><td>男医生</td><td>👩‍⚕️</td><td>女医生</td></tr><tr><td>👨‍🎓</td><td>男学生</td><td>👩‍🎓</td><td>女学生</td><td>👨‍🏫</td><td>男老师</td></tr><tr><td>👩‍🏫</td><td>女老师</td><td>👨‍⚖️</td><td>男法官</td><td>👩‍⚖️</td><td>女法官</td></tr><tr><td>👨‍🌾</td><td>农夫</td><td>👩‍🌾</td><td>农妇</td><td>👨‍🍳</td><td>男厨师</td></tr><tr><td>👩‍🍳</td><td>女厨师</td><td>👨‍🔧</td><td>男技工</td><td>👩‍🔧</td><td>女技工</td></tr><tr><td>👨‍🏭</td><td>男工人</td><td>👩‍🏭</td><td>女工人</td><td>👨‍💼</td><td>男白领</td></tr><tr><td>👩‍💼</td><td>女白领</td><td>👨‍🔬</td><td>男科学家</td><td>👩‍🔬</td><td>女科学家</td></tr><tr><td>👨‍💻</td><td>男程序员</td><td>👩‍💻</td><td>女程序员</td><td>👨‍🎤</td><td>男歌手</td></tr><tr><td>👩‍🎤</td><td>女歌手</td><td>👨‍🎨</td><td>男艺术家</td><td>👩‍🎨</td><td>女艺术家</td></tr><tr><td>👨‍✈️</td><td>男飞行员</td><td>👩‍✈️</td><td>女飞行员</td><td>👨‍🚀</td><td>男宇航员</td></tr><tr><td>👩‍🚀</td><td>女宇航员</td><td>👨‍🚒</td><td>男消防员</td><td>👩‍🚒</td><td>女消防员</td></tr><tr><td>👮</td><td>警察</td><td>👮‍♂️</td><td>男警察</td><td>👮‍♀️</td><td>女警察</td></tr><tr><td>🕵️</td><td>侦探</td><td>🕵️‍♂️</td><td>男侦探</td><td>🕵️‍♀️</td><td>女侦探</td></tr><tr><td>💂</td><td>卫兵</td><td>💂‍♂️</td><td>男卫兵</td><td>💂‍♀️</td><td>女卫兵</td></tr><tr><td>👷</td><td>建筑工人</td><td>👷‍♂️</td><td>男建筑工人</td><td>👷‍♀️</td><td>女建筑工人</td></tr><tr><td>🤴</td><td>王子</td><td>👸</td><td>公主</td><td>👳</td><td>戴头巾的人</td></tr><tr><td>👳‍♂️</td><td>戴头巾的男人</td><td>👳‍♀️</td><td>戴头巾的女人</td><td>👲</td><td>戴瓜皮帽的人</td></tr><tr><td>🧕</td><td>带头饰的女人</td><td>🤵</td><td>穿燕尾服的男人</td><td>👰</td><td>戴头纱的新娘</td></tr><tr><td>🤰</td><td>孕妇</td><td>🤱</td><td>母乳喂养</td><td>👼</td><td>小天使</td></tr><tr><td>🎅</td><td>圣诞老人</td><td>🤶</td><td>圣诞奶奶</td><td>🦸</td><td>超级英雄</td></tr><tr><td>🦸‍♂️</td><td>男超级英雄</td><td>🦸‍♀️</td><td>女超级英雄</td><td>🦹</td><td>超级大坏蛋</td></tr><tr><td>🦹‍♂️</td><td>男超级大坏蛋</td><td>🦹‍♀️</td><td>女超级大坏蛋</td><td>🧙</td><td>法师</td></tr><tr><td>🧙‍♂️</td><td>男法师</td><td>🧙‍♀️</td><td>女法师</td><td>🧚</td><td>精灵</td></tr><tr><td>🧚‍♂️</td><td>仙人</td><td>🧚‍♀️</td><td>仙女</td><td>🧛</td><td>吸血鬼</td></tr><tr><td>🧛‍♂️</td><td>男吸血鬼</td><td>🧛‍♀️</td><td>女吸血鬼</td><td>🧜</td><td>人鱼</td></tr><tr><td>🧜‍♂️</td><td>男人鱼</td><td>🧜‍♀️</td><td>美人鱼</td><td>🧝</td><td>小精灵</td></tr><tr><td>🧝‍♂️</td><td>男小精灵</td><td>🧝‍♀️</td><td>女小精灵</td><td>🧞</td><td>妖怪</td></tr><tr><td>🧞‍♂️</td><td>男妖怪</td><td>🧞‍♀️</td><td>女妖怪</td><td>🧟</td><td>僵尸</td></tr><tr><td>🧟‍♂️</td><td>男僵尸</td><td>🧟‍♀️</td><td>女僵尸</td><td>💆</td><td>按摩</td></tr><tr><td>💆‍♂️</td><td>男生按摩</td><td>💆‍♀️</td><td>女生按摩</td><td>💇</td><td>理发</td></tr><tr><td>💇‍♂️</td><td>男生理发</td><td>💇‍♀️</td><td>女生理发</td><td>🚶</td><td>行人</td></tr><tr><td>🚶‍♂️</td><td>男行人</td><td>🚶‍♀️</td><td>女行人</td><td>🧍</td><td>站立者</td></tr><tr><td>🧍‍♂️</td><td>站立的男人</td><td>🧍‍♀️</td><td>站立的女人</td><td>🧎</td><td>下跪者</td></tr><tr><td>🧎‍♂️</td><td>跪下的男人</td><td>🧎‍♀️</td><td>跪下的女人</td><td>👨‍🦯</td><td>拄盲杖的男人</td></tr><tr><td>👩‍🦯</td><td>拄盲杖的女人</td><td>👨‍🦼</td><td>坐电动轮椅的男人</td><td>👩‍🦼</td><td>坐电动轮椅的女人</td></tr><tr><td>👨‍🦽</td><td>坐手动轮椅的男人</td><td>👩‍🦽</td><td>坐手动轮椅的女人</td><td>🏃</td><td>跑步者</td></tr><tr><td>🏃‍♂️</td><td>男生跑步</td><td>🏃‍♀️</td><td>女生跑步</td><td>💃</td><td>跳舞的女人</td></tr><tr><td>🕺</td><td>跳舞的男人</td><td>🕴️</td><td>西装革履的人</td><td>👯</td><td>戴兔耳朵的人</td></tr><tr><td>👯‍♂️</td><td>兔先生</td><td>👯‍♀️</td><td>兔女郎</td><td>🧖</td><td>蒸房里的人</td></tr><tr><td>🧖‍♂️</td><td>蒸房里的男人</td><td>🧖‍♀️</td><td>蒸房里的女人</td><td>🧗</td><td>攀爬的人</td></tr><tr><td>🧗‍♂️</td><td>攀爬的男人</td><td>🧗‍♀️</td><td>攀爬的女人</td><td>🤺</td><td>击剑选手</td></tr><tr><td>🏇</td><td>赛马</td><td>⛷️</td><td>滑雪的人</td><td>🏂</td><td>滑雪板</td></tr><tr><td>🏌️</td><td>打高尔夫的人</td><td>🏌️‍♂️</td><td>男生打高尔夫</td><td>🏌️‍♀️</td><td>女生打高尔夫</td></tr><tr><td>🏄</td><td>冲浪</td><td>🏄‍♂️</td><td>男生冲浪</td><td>🏄‍♀️</td><td>女生冲浪</td></tr><tr><td>🚣</td><td>划艇</td><td>🚣‍♂️</td><td>男生划船</td><td>🚣‍♀️</td><td>女生划船</td></tr><tr><td>🏊</td><td>游泳</td><td>🏊‍♂️</td><td>男生游泳</td><td>🏊‍♀️</td><td>女生游泳</td></tr><tr><td>⛹️</td><td>玩球</td><td>⛹️‍♂️</td><td>男生玩球</td><td>⛹️‍♀️</td><td>女生玩球</td></tr><tr><td>🏋️</td><td>举重</td><td>🏋️‍♂️</td><td>男生举重</td><td>🏋️‍♀️</td><td>女生举重</td></tr><tr><td>🚴</td><td>骑自行车</td><td>🚴‍♂️</td><td>男生骑自行车</td><td>🚴‍♀️</td><td>女生骑自行车</td></tr><tr><td>🚵</td><td>骑山地车</td><td>🚵‍♂️</td><td>男生骑山地车</td><td>🚵‍♀️</td><td>女生骑山地车</td></tr><tr><td>🤸</td><td>侧手翻</td><td>🤸‍♂️</td><td>男生侧手翻</td><td>🤸‍♀️</td><td>女生侧手翻</td></tr><tr><td>🤼</td><td>摔跤选手</td><td>🤼‍♂️</td><td>男生摔跤</td><td>🤼‍♀️</td><td>女生摔跤</td></tr><tr><td>🤽</td><td>水球</td><td>🤽‍♂️</td><td>男生玩水球</td><td>🤽‍♀️</td><td>女生玩水球</td></tr><tr><td>🤾</td><td>手球</td><td>🤾‍♂️</td><td>男生玩手球</td><td>🤾‍♀️</td><td>女生玩手球</td></tr><tr><td>🤹</td><td>抛接杂耍</td><td>🤹‍♂️</td><td>男生抛接杂耍</td><td>🤹‍♀️</td><td>女生抛接杂耍</td></tr><tr><td>🧘</td><td>盘腿的人</td><td>🧘‍♂️</td><td>盘腿的男人</td><td>🧘‍♀️</td><td>盘腿的女人</td></tr><tr><td>🛀</td><td>洗澡的人</td><td>🛌</td><td>躺在床上的人</td><td>🧑‍🤝‍🧑</td><td>手拉手的两个人</td></tr><tr><td>🧑🏻‍🤝‍🧑🏻</td><td>手拉手的两个人<br>较浅肤色</td><td>🧑🏼‍🤝‍🧑🏻</td><td>手拉手的两个人<br>中等-浅肤色较浅肤色</td><td>🧑🏼‍🤝‍🧑🏼</td><td>手拉手的两个人<br>中等-浅肤色</td></tr><tr><td>🧑🏽‍🤝‍🧑🏻</td><td>手拉手的两个人<br>中等肤色较浅肤色</td><td>🧑🏽‍🤝‍🧑🏼</td><td>手拉手的两个人<br>中等肤色中等-浅肤色</td><td>🧑🏽‍🤝‍🧑🏽</td><td>手拉手的两个人<br>中等肤色</td></tr><tr><td>🧑🏾‍🤝‍🧑🏻</td><td>手拉手的两个人<br>中等-深肤色较浅肤色</td><td>🧑🏾‍🤝‍🧑🏼</td><td>手拉手的两个人<br>中等-深肤色中等-浅肤色</td><td>🧑🏾‍🤝‍🧑🏽</td><td>手拉手的两个人<br>中等-深肤色中等肤色</td></tr><tr><td>🧑🏾‍🤝‍🧑🏾</td><td>手拉手的两个人<br>中等-深肤色</td><td>🧑🏿‍🤝‍🧑🏻</td><td>手拉手的两个人<br>较深肤色较浅肤色</td><td>🧑🏿‍🤝‍🧑🏼</td><td>手拉手的两个人<br>较深肤色中等-浅肤色</td></tr><tr><td>🧑🏿‍🤝‍🧑🏽</td><td>手拉手的两个人<br>较深肤色中等肤色</td><td>🧑🏿‍🤝‍🧑🏾</td><td>手拉手的两个人<br>较深肤色中等-深肤色</td><td>🧑🏿‍🤝‍🧑🏿</td><td>手拉手的两个人<br>较深肤色</td></tr><tr><td>👭</td><td>手拉手的两个女人</td><td>👭🏻</td><td>手拉手的两个女人<br>较浅肤色</td><td>👩🏼‍🤝‍👩🏻</td><td>手拉手的两个女人<br>中等-浅肤色较浅肤色</td></tr><tr><td>👭🏼</td><td>手拉手的两个女人<br>中等-浅肤色</td><td>👩🏽‍🤝‍👩🏻</td><td>手拉手的两个女人<br>中等肤色较浅肤色</td><td>👩🏽‍🤝‍👩🏼</td><td>手拉手的两个女人<br>中等肤色中等-浅肤色</td></tr><tr><td>👭🏽</td><td>手拉手的两个女人<br>中等肤色</td><td>👩🏾‍🤝‍👩🏻</td><td>手拉手的两个女人<br>中等-深肤色较浅肤色</td><td>👩🏾‍🤝‍👩🏼</td><td>手拉手的两个女人<br>中等-深肤色中等-浅肤色</td></tr><tr><td>👩🏾‍🤝‍👩🏽</td><td>手拉手的两个女人<br>中等-深肤色中等肤色</td><td>👭🏾</td><td>手拉手的两个女人<br>中等-深肤色</td><td>👩🏿‍🤝‍👩🏻</td><td>手拉手的两个女人<br>较深肤色较浅肤色</td></tr><tr><td>👩🏿‍🤝‍👩🏼</td><td>手拉手的两个女人<br>较深肤色中等-浅肤色</td><td>👩🏿‍🤝‍👩🏽</td><td>手拉手的两个女人<br>较深肤色中等肤色</td><td>👩🏿‍🤝‍👩🏾</td><td>手拉手的两个女人<br>较深肤色中等-深肤色</td></tr><tr><td>👭🏿</td><td>手拉手的两个女人<br>较深肤色</td><td>👫</td><td>手拉手的一男一女</td><td>👫🏻</td><td>手拉手的一男一女<br>较浅肤色</td></tr><tr><td>👩🏻‍🤝‍👨🏼</td><td>手拉手的一男一女<br>较浅肤色中等-浅肤色</td><td>👩🏻‍🤝‍👨🏽</td><td>手拉手的一男一女<br>较浅肤色中等肤色</td><td>👩🏻‍🤝‍👨🏾</td><td>手拉手的一男一女<br>较浅肤色中等-深肤色</td></tr><tr><td>👩🏻‍🤝‍👨🏿</td><td>手拉手的一男一女<br>较浅肤色较深肤色</td><td>👩🏼‍🤝‍👨🏻</td><td>手拉手的一男一女<br>中等-浅肤色较浅肤色</td><td>👫🏼</td><td>手拉手的一男一女<br>中等-浅肤色</td></tr><tr><td>👩🏼‍🤝‍👨🏽</td><td>手拉手的一男一女<br>中等-浅肤色中等肤色</td><td>👩🏼‍🤝‍👨🏾</td><td>手拉手的一男一女<br>中等-浅肤色中等-深肤色</td><td>👩🏼‍🤝‍👨🏿</td><td>手拉手的一男一女<br>中等-浅肤色较深肤色</td></tr><tr><td>👩🏽‍🤝‍👨🏻</td><td>手拉手的一男一女<br>中等肤色较浅肤色</td><td>👩🏽‍🤝‍👨🏼</td><td>手拉手的一男一女<br>中等肤色中等-浅肤色</td><td>👫🏽</td><td>手拉手的一男一女<br>中等肤色</td></tr><tr><td>👩🏽‍🤝‍👨🏾</td><td>手拉手的一男一女<br>中等肤色中等-深肤色</td><td>👩🏽‍🤝‍👨🏿</td><td>手拉手的一男一女<br>中等肤色较深肤色</td><td>👩🏾‍🤝‍👨🏻</td><td>手拉手的一男一女<br>中等-深肤色较浅肤色</td></tr><tr><td>👩🏾‍🤝‍👨🏼</td><td>手拉手的一男一女<br>中等-深肤色中等-浅肤色</td><td>👩🏾‍🤝‍👨🏽</td><td>手拉手的一男一女<br>中等-深肤色中等肤色</td><td>👫🏾</td><td>手拉手的一男一女<br>中等-深肤色</td></tr><tr><td>👩🏾‍🤝‍👨🏿</td><td>手拉手的一男一女<br>中等-深肤色较深肤色</td><td>👩🏿‍🤝‍👨🏻</td><td>手拉手的一男一女<br>较深肤色较浅肤色</td><td>👩🏿‍🤝‍👨🏼</td><td>手拉手的一男一女<br>较深肤色中等-浅肤色</td></tr><tr><td>👩🏿‍🤝‍👨🏽</td><td>手拉手的一男一女<br>较深肤色中等肤色</td><td>👩🏿‍🤝‍👨🏾</td><td>手拉手的一男一女<br>较深肤色中等-深肤色</td><td>👫🏿</td><td>手拉手的一男一女<br>较深肤色</td></tr><tr><td>👬</td><td>手拉手的两个男人</td><td>👬🏻</td><td>手拉手的两个男人<br>较浅肤色</td><td>👨🏼‍🤝‍👨🏻</td><td>手拉手的两个男人<br>中等-浅肤色较浅肤色</td></tr><tr><td>👬🏼</td><td>手拉手的两个男人<br>中等-浅肤色</td><td>👨🏽‍🤝‍👨🏻</td><td>手拉手的两个男人<br>中等肤色较浅肤色</td><td>👨🏽‍🤝‍👨🏼</td><td>手拉手的两个男人<br>中等肤色中等-浅肤色</td></tr><tr><td>👬🏽</td><td>手拉手的两个男人<br>中等肤色</td><td>👨🏾‍🤝‍👨🏻</td><td>手拉手的两个男人<br>中等-深肤色较浅肤色</td><td>👨🏾‍🤝‍👨🏼</td><td>手拉手的两个男人<br>中等-深肤色中等-浅肤色</td></tr><tr><td>👨🏾‍🤝‍👨🏽</td><td>手拉手的两个男人<br>中等-深肤色中等肤色</td><td>👬🏾</td><td>手拉手的两个男人<br>中等-深肤色</td><td>👨🏿‍🤝‍👨🏻</td><td>手拉手的两个男人<br>较深肤色较浅肤色</td></tr><tr><td>👨🏿‍🤝‍👨🏼</td><td>手拉手的两个男人<br>较深肤色中等-浅肤色</td><td>👨🏿‍🤝‍👨🏽</td><td>手拉手的两个男人<br>较深肤色中等肤色</td><td>👨🏿‍🤝‍👨🏾</td><td>手拉手的两个男人<br>较深肤色中等-深肤色</td></tr><tr><td>👬🏿</td><td>手拉手的两个男人<br>较深肤色</td><td>💏</td><td>亲吻</td><td>👩‍❤️‍💋‍👨</td><td>亲吻<br>女人男人</td></tr><tr><td>👨‍❤️‍💋‍👨</td><td>亲吻<br>男人男人</td><td>👩‍❤️‍💋‍👩</td><td>亲吻<br>女人女人</td><td>💑</td><td>情侣</td></tr><tr><td>👩‍❤️‍👨</td><td>情侣<br>女人男人</td><td>👨‍❤️‍👨</td><td>情侣<br>男人男人</td><td>👩‍❤️‍👩</td><td>情侣<br>女人女人</td></tr><tr><td>👪</td><td>家庭</td><td>👨‍👩‍👦</td><td>家庭<br>男人女人男孩</td><td>👨‍👩‍👧</td><td>家庭<br>男人女人女孩</td></tr><tr><td>👨‍👩‍👧‍👦</td><td>家庭<br>男人女人女孩男孩</td><td>👨‍👩‍👦‍👦</td><td>家庭<br>男人女人男孩男孩</td><td>👨‍👩‍👧‍👧</td><td>家庭<br>男人女人女孩女孩</td></tr><tr><td>👨‍👨‍👦</td><td>家庭<br>男人男人男孩</td><td>👨‍👨‍👧</td><td>家庭<br>男人男人女孩</td><td>👨‍👨‍👧‍👦</td><td>家庭<br>男人男人女孩男孩</td></tr><tr><td>👨‍👨‍👦‍👦</td><td>家庭<br>男人男人男孩男孩</td><td>👨‍👨‍👧‍👧</td><td>家庭<br>男人男人女孩女孩</td><td>👩‍👩‍👦</td><td>家庭<br>女人女人男孩</td></tr><tr><td>👩‍👩‍👧</td><td>家庭<br>女人女人女孩</td><td>👩‍👩‍👧‍👦</td><td>家庭<br>女人女人女孩男孩</td><td>👩‍👩‍👦‍👦</td><td>家庭<br>女人女人男孩男孩</td></tr><tr><td>👩‍👩‍👧‍👧</td><td>家庭<br>女人女人女孩女孩</td><td>👨‍👦</td><td>家庭<br>男人男孩</td><td>👨‍👦‍👦</td><td>家庭<br>男人男孩男孩</td></tr><tr><td>👨‍👧</td><td>家庭<br>男人女孩</td><td>👨‍👧‍👦</td><td>家庭<br>男人女孩男孩</td><td>👨‍👧‍👧</td><td>家庭<br>男人女孩女孩</td></tr><tr><td>👩‍👦</td><td>家庭<br>女人男孩</td><td>👩‍👦‍👦</td><td>家庭<br>女人男孩男孩</td><td>👩‍👧</td><td>家庭<br>女人女孩</td></tr><tr><td>👩‍👧‍👦</td><td>家庭<br>女人女孩男孩</td><td>👩‍👧‍👧</td><td>家庭<br>女人女孩女孩</td><td>🗣️</td><td>说话</td></tr><tr><td>👤</td><td>人像</td><td>👥</td><td>双人像</td><td>👣</td><td>脚印</td></tr></tbody></table></div><h2 id="动物和自然"><a href="#动物和自然" class="headerlink" title="动物和自然"></a>动物和自然</h2><div class="table-container"><table><thead><tr><th>表情</th><th>名称</th><th>表情</th><th>名称</th><th>表情</th><th>名称</th></tr></thead><tbody><tr><td>🐵</td><td>猴头</td><td>🐒</td><td>猴子</td><td>🦍</td><td>大猩猩</td></tr><tr><td>🦧</td><td>红毛猩猩</td><td>🐶</td><td>狗脸</td><td>🐕</td><td>狗</td></tr><tr><td>🦮</td><td>导盲犬</td><td>🐕‍🦺</td><td>服务犬</td><td>🐩</td><td>贵宾犬</td></tr><tr><td>🐺</td><td>狼</td><td>🦊</td><td>狐狸</td><td>🦝</td><td>浣熊</td></tr><tr><td>🐱</td><td>猫脸</td><td>🐈</td><td>猫</td><td>🦁</td><td>狮子</td></tr><tr><td>🐯</td><td>老虎头</td><td>🐅</td><td>老虎</td><td>🐆</td><td>豹子</td></tr><tr><td>🐴</td><td>马头</td><td>🐎</td><td>马</td><td>🦄</td><td>独角兽</td></tr><tr><td>🦓</td><td>斑马</td><td>🦌</td><td>鹿</td><td>🐮</td><td>奶牛头</td></tr><tr><td>🐂</td><td>公牛</td><td>🐃</td><td>水牛</td><td>🐄</td><td>奶牛</td></tr><tr><td>🐷</td><td>猪头</td><td>🐖</td><td>猪</td><td>🐗</td><td>野猪</td></tr><tr><td>🐽</td><td>猪鼻子</td><td>🐏</td><td>公羊</td><td>🐑</td><td>母羊</td></tr><tr><td>🐐</td><td>山羊</td><td>🐪</td><td>骆驼</td><td>🐫</td><td>双峰骆驼</td></tr><tr><td>🦙</td><td>美洲鸵</td><td>🦒</td><td>长颈鹿</td><td>🐘</td><td>大象</td></tr><tr><td>🦏</td><td>犀牛</td><td>🦛</td><td>河马</td><td>🐭</td><td>老鼠头</td></tr><tr><td>🐁</td><td>老鼠</td><td>🐀</td><td>耗子</td><td>🐹</td><td>仓鼠</td></tr><tr><td>🐰</td><td>兔子头</td><td>🐇</td><td>兔子</td><td>🐿️</td><td>松鼠</td></tr><tr><td>🦔</td><td>刺猬</td><td>🦇</td><td>蝙蝠</td><td>🐻</td><td>熊</td></tr><tr><td>🐨</td><td>考拉</td><td>🐼</td><td>熊猫</td><td>🦥</td><td>树懒</td></tr><tr><td>🦦</td><td>水獭</td><td>🦨</td><td>臭鼬</td><td>🦘</td><td>袋鼠</td></tr><tr><td>🦡</td><td>獾</td><td>🐾</td><td>爪印</td><td>🦃</td><td>火鸡</td></tr><tr><td>🐔</td><td>鸡</td><td>🐓</td><td>公鸡</td><td>🐣</td><td>小鸡破壳</td></tr><tr><td>🐤</td><td>小鸡</td><td>🐥</td><td>正面朝向的小鸡</td><td>🐦</td><td>鸟</td></tr><tr><td>🐧</td><td>企鹅</td><td>🕊️</td><td>鸽</td><td>🦅</td><td>鹰</td></tr><tr><td>🦆</td><td>鸭子</td><td>🦢</td><td>天鹅</td><td>🦉</td><td>猫头鹰</td></tr><tr><td>🦩</td><td>火烈鸟</td><td>🦚</td><td>孔雀</td><td>🦜</td><td>鹦鹉</td></tr><tr><td>🐸</td><td>青蛙</td><td>🐊</td><td>鳄鱼</td><td>🐢</td><td>龟</td></tr><tr><td>🦎</td><td>蜥蜴</td><td>🐍</td><td>蛇</td><td>🐲</td><td>龙头</td></tr><tr><td>🐉</td><td>龙</td><td>🦕</td><td>蜥蜴类</td><td>🦖</td><td>霸王龙</td></tr><tr><td>🐳</td><td>喷水的鲸</td><td>🐋</td><td>鲸鱼</td><td>🐬</td><td>海豚</td></tr><tr><td>🐟</td><td>鱼</td><td>🐠</td><td>热带鱼</td><td>🐡</td><td>河豚</td></tr><tr><td>🦈</td><td>鲨鱼</td><td>🐙</td><td>章鱼</td><td>🐚</td><td>海螺</td></tr><tr><td>🐌</td><td>蜗牛</td><td>🦋</td><td>蝴蝶</td><td>🐛</td><td>毛毛虫</td></tr><tr><td>🐜</td><td>蚂蚁</td><td>🐝</td><td>蜜蜂</td><td>🐞</td><td>瓢虫</td></tr><tr><td>🦗</td><td>蟋蟀</td><td>🕷️</td><td>蜘蛛</td><td>🕸️</td><td>蜘蛛网</td></tr><tr><td>🦂</td><td>蝎子</td><td>🦟</td><td>蚊子</td><td>🦠</td><td>细菌</td></tr><tr><td>💐</td><td>花束</td><td>🌸</td><td>樱花</td><td>💮</td><td>白花</td></tr><tr><td>🏵️</td><td>圆形花饰</td><td>🌹</td><td>玫瑰</td><td>🥀</td><td>枯萎的花</td></tr><tr><td>🌺</td><td>芙蓉</td><td>🌻</td><td>向日葵</td><td>🌼</td><td>开花</td></tr><tr><td>🌷</td><td>郁金香</td><td>🌱</td><td>幼苗</td><td>🌲</td><td>松树</td></tr><tr><td>🌳</td><td>落叶树</td><td>🌴</td><td>棕榈树</td><td>🌵</td><td>仙人掌</td></tr><tr><td>🌾</td><td>稻子</td><td>🌿</td><td>药草</td><td>☘️</td><td>三叶草</td></tr><tr><td>🍀</td><td>四叶草</td><td>🍁</td><td>枫叶</td><td>🍂</td><td>落叶</td></tr><tr><td>🍃</td><td>风吹叶落</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table></div><h2 id="食物和饮料"><a href="#食物和饮料" class="headerlink" title="食物和饮料"></a>食物和饮料</h2><div class="table-container"><table><thead><tr><th>表情</th><th>名称</th><th>表情</th><th>名称</th><th>表情</th><th>名称</th></tr></thead><tbody><tr><td>🍇</td><td>葡萄</td><td>🍈</td><td>甜瓜</td><td>🍉</td><td>西瓜</td></tr><tr><td>🍊</td><td>橘子</td><td>🍋</td><td>柠檬</td><td>🍌</td><td>香蕉</td></tr><tr><td>🍍</td><td>菠萝</td><td>🥭</td><td>芒果</td><td>🍎</td><td>红苹果</td></tr><tr><td>🍏</td><td>青苹果</td><td>🍐</td><td>梨</td><td>🍑</td><td>桃</td></tr><tr><td>🍒</td><td>樱桃</td><td>🍓</td><td>草莓</td><td>🥝</td><td>猕猴桃</td></tr><tr><td>🍅</td><td>西红柿</td><td>🥥</td><td>椰子</td><td>🥑</td><td>鳄梨</td></tr><tr><td>🍆</td><td>茄子</td><td>🥔</td><td>土豆</td><td>🥕</td><td>胡萝卜</td></tr><tr><td>🌽</td><td>玉米</td><td>🌶️</td><td>红辣椒</td><td>🥒</td><td>黄瓜</td></tr><tr><td>🥬</td><td>绿叶蔬菜</td><td>🥦</td><td>西兰花</td><td>🧄</td><td>蒜</td></tr><tr><td>🧅</td><td>洋葱</td><td>🍄</td><td>蘑菇</td><td>🥜</td><td>花生</td></tr><tr><td>🌰</td><td>栗子</td><td>🍞</td><td>面包</td><td>🥐</td><td>羊角面包</td></tr><tr><td>🥖</td><td>法式长棍面包</td><td>🥨</td><td>椒盐卷饼</td><td>🥯</td><td>面包圈</td></tr><tr><td>🥞</td><td>烙饼</td><td>🧇</td><td>华夫饼</td><td>🧀</td><td>芝士</td></tr><tr><td>🍖</td><td>排骨</td><td>🍗</td><td>家禽的腿</td><td>🥩</td><td>肉块</td></tr><tr><td>🥓</td><td>培根</td><td>🍔</td><td>汉堡</td><td>🍟</td><td>薯条</td></tr><tr><td>🍕</td><td>披萨</td><td>🌭</td><td>热狗</td><td>🥪</td><td>三明治</td></tr><tr><td>🌮</td><td>墨西哥卷饼</td><td>🌯</td><td>墨西哥玉米煎饼</td><td>🥙</td><td>夹心饼</td></tr><tr><td>🧆</td><td>炸豆丸子</td><td>🥚</td><td>蛋</td><td>🍳</td><td>煎蛋</td></tr><tr><td>🥘</td><td>装有食物的浅底锅</td><td>🍲</td><td>一锅食物</td><td>🥣</td><td>碗勺</td></tr><tr><td>🥗</td><td>绿色沙拉</td><td>🍿</td><td>爆米花</td><td>🧈</td><td>黄油</td></tr><tr><td>🧂</td><td>盐</td><td>🥫</td><td>罐头食品</td><td>🍱</td><td>盒饭</td></tr><tr><td>🍘</td><td>米饼</td><td>🍙</td><td>饭团</td><td>🍚</td><td>米饭</td></tr><tr><td>🍛</td><td>咖喱饭</td><td>🍜</td><td>面条</td><td>🍝</td><td>意粉</td></tr><tr><td>🍠</td><td>烤红薯</td><td>🍢</td><td>关东煮</td><td>🍣</td><td>寿司</td></tr><tr><td>🍤</td><td>天妇罗</td><td>🍥</td><td>鱼板</td><td>🥮</td><td>月饼</td></tr><tr><td>🍡</td><td>团子</td><td>🥟</td><td>饺子</td><td>🥠</td><td>幸运饼干</td></tr><tr><td>🥡</td><td>外卖盒</td><td>🦀</td><td>蟹</td><td>🦞</td><td>龙虾</td></tr><tr><td>🦐</td><td>虾</td><td>🦑</td><td>乌贼</td><td>🦪</td><td>牡蛎</td></tr><tr><td>🍦</td><td>圆筒冰激凌</td><td>🍧</td><td>刨冰</td><td>🍨</td><td>冰淇淋</td></tr><tr><td>🍩</td><td>甜甜圈</td><td>🍪</td><td>饼干</td><td>🎂</td><td>生日蛋糕</td></tr><tr><td>🍰</td><td>水果蛋糕</td><td>🧁</td><td>纸杯蛋糕</td><td>🥧</td><td>派</td></tr><tr><td>🍫</td><td>巧克力</td><td>🍬</td><td>糖</td><td>🍭</td><td>棒棒糖</td></tr><tr><td>🍮</td><td>奶黄</td><td>🍯</td><td>蜂蜜</td><td>🍼</td><td>奶瓶</td></tr><tr><td>🥛</td><td>一杯奶</td><td>☕</td><td>热饮</td><td>🍵</td><td>热茶</td></tr><tr><td>🍶</td><td>清酒</td><td>🍾</td><td>开香槟</td><td>🍷</td><td>葡萄酒</td></tr><tr><td>🍸</td><td>鸡尾酒</td><td>🍹</td><td>热带水果饮料</td><td>🍺</td><td>啤酒</td></tr><tr><td>🍻</td><td>干杯</td><td>🥂</td><td>碰杯</td><td>🥃</td><td>平底杯</td></tr><tr><td>🥤</td><td>带吸管杯</td><td>🧃</td><td>饮料盒</td><td>🧉</td><td>马黛茶</td></tr><tr><td>🧊</td><td>冰块</td><td>🥢</td><td>筷子</td><td>🍽️</td><td>餐具</td></tr><tr><td>🍴</td><td>刀叉</td><td>🥄</td><td>匙</td><td>🔪</td><td>菜刀</td></tr><tr><td>🏺</td><td>双耳瓶</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table></div><h2 id="旅行和地点"><a href="#旅行和地点" class="headerlink" title="旅行和地点"></a>旅行和地点</h2><div class="table-container"><table><thead><tr><th>表情</th><th>名称</th><th>表情</th><th>名称</th><th>表情</th><th>名称</th></tr></thead><tbody><tr><td>🌍</td><td>地球上的欧洲非洲</td><td>🌎</td><td>地球上的美洲</td><td>🌏</td><td>地球上的亚洲澳洲</td></tr><tr><td>🌐</td><td>带经纬线的地球</td><td>🗺️</td><td>世界地图</td><td>🗾</td><td>日本地图</td></tr><tr><td>🧭</td><td>指南针</td><td>🏔️</td><td>雪山</td><td>⛰️</td><td>山</td></tr><tr><td>🌋</td><td>火山</td><td>🗻</td><td>富士山</td><td>🏕️</td><td>露营</td></tr><tr><td>🏖️</td><td>沙滩伞</td><td>🏜️</td><td>沙漠</td><td>🏝️</td><td>无人荒岛</td></tr><tr><td>🏞️</td><td>国家公园</td><td>🏟️</td><td>体育馆</td><td>🏛️</td><td>古典建筑</td></tr><tr><td>🏗️</td><td>施工</td><td>🧱</td><td>砖</td><td>🏘️</td><td>房屋建筑</td></tr><tr><td>🏚️</td><td>废墟</td><td>🏠</td><td>房子</td><td>🏡</td><td>别墅</td></tr><tr><td>🏢</td><td>办公楼</td><td>🏣</td><td>日本邮局</td><td>🏤</td><td>邮局</td></tr><tr><td>🏥</td><td>医院</td><td>🏦</td><td>银行</td><td>🏨</td><td>酒店</td></tr><tr><td>🏩</td><td>情人酒店</td><td>🏪</td><td>便利店</td><td>🏫</td><td>学校</td></tr><tr><td>🏬</td><td>商场</td><td>🏭</td><td>工厂</td><td>🏯</td><td>日本城堡</td></tr><tr><td>🏰</td><td>欧洲城堡</td><td>💒</td><td>婚礼</td><td>🗼</td><td>东京塔</td></tr><tr><td>🗽</td><td>自由女神像</td><td>⛪</td><td>教堂</td><td>🕌</td><td>清真寺</td></tr><tr><td>🛕</td><td>印度寺庙</td><td>🕍</td><td>犹太教堂</td><td>⛩️</td><td>神社</td></tr><tr><td>🕋</td><td>克尔白</td><td>⛲</td><td>喷泉</td><td>⛺</td><td>帐篷</td></tr><tr><td>🌁</td><td>有雾</td><td>🌃</td><td>夜晚</td><td>🏙️</td><td>城市风光</td></tr><tr><td>🌄</td><td>山顶日出</td><td>🌅</td><td>日出</td><td>🌆</td><td>城市黄昏</td></tr><tr><td>🌇</td><td>日落</td><td>🌉</td><td>夜幕下的桥</td><td>♨️</td><td>温泉</td></tr><tr><td>🎠</td><td>旋转木马</td><td>🎡</td><td>摩天轮</td><td>🎢</td><td>过山车</td></tr><tr><td>💈</td><td>理发店</td><td>🎪</td><td>马戏团帐篷</td><td>🚂</td><td>蒸汽火车</td></tr><tr><td>🚃</td><td>轨道车</td><td>🚄</td><td>高速列车</td><td>🚅</td><td>子弹头高速列车</td></tr><tr><td>🚆</td><td>火车</td><td>🚇</td><td>地铁</td><td>🚈</td><td>轻轨</td></tr><tr><td>🚉</td><td>车站</td><td>🚊</td><td>路面电车</td><td>🚝</td><td>单轨</td></tr><tr><td>🚞</td><td>山区铁路</td><td>🚋</td><td>有轨电车</td><td>🚌</td><td>公交车</td></tr><tr><td>🚍</td><td>迎面驶来的公交车</td><td>🚎</td><td>无轨电车</td><td>🚐</td><td>小巴</td></tr><tr><td>🚑</td><td>救护车</td><td>🚒</td><td>消防车</td><td>🚓</td><td>警车</td></tr><tr><td>🚔</td><td>迎面驶来的警车</td><td>🚕</td><td>出租车</td><td>🚖</td><td>迎面驶来的出租车</td></tr><tr><td>🚗</td><td>汽车</td><td>🚘</td><td>迎面驶来的汽车</td><td>🚙</td><td>运动型多用途车</td></tr><tr><td>🚚</td><td>货车</td><td>🚛</td><td>铰接式货车</td><td>🚜</td><td>拖拉机</td></tr><tr><td>🏎️</td><td>赛车</td><td>🏍️</td><td>摩托车</td><td>🛵</td><td>小型摩托车</td></tr><tr><td>🦽</td><td>手动轮椅</td><td>🦼</td><td>电动轮椅</td><td>🛺</td><td>三轮摩托车</td></tr><tr><td>🚲</td><td>自行车</td><td>🛴</td><td>滑板车</td><td>🛹</td><td>滑板</td></tr><tr><td>🚏</td><td>公交车站</td><td>🛣️</td><td>高速公路</td><td>🛤️</td><td>铁轨</td></tr><tr><td>🛢️</td><td>石油桶</td><td>⛽</td><td>油泵</td><td>🚨</td><td>警车灯</td></tr><tr><td>🚥</td><td>横向的红绿灯</td><td>🚦</td><td>纵向的红绿灯</td><td>🛑</td><td>停止标志</td></tr><tr><td>🚧</td><td>路障</td><td>⚓</td><td>锚</td><td>⛵</td><td>帆船</td></tr><tr><td>🛶</td><td>独木舟</td><td>🚤</td><td>快艇</td><td>🛳️</td><td>客轮</td></tr><tr><td>⛴️</td><td>渡轮</td><td>🛥️</td><td>摩托艇</td><td>🚢</td><td>船</td></tr><tr><td>✈️</td><td>飞机</td><td>🛩️</td><td>小型飞机</td><td>🛫</td><td>航班起飞</td></tr><tr><td>🛬</td><td>航班降落</td><td>🪂</td><td>降落伞</td><td>💺</td><td>座位</td></tr><tr><td>🚁</td><td>直升机</td><td>🚟</td><td>空轨</td><td>🚠</td><td>缆车</td></tr><tr><td>🚡</td><td>索道</td><td>🛰️</td><td>卫星</td><td>🚀</td><td>火箭</td></tr><tr><td>🛸</td><td>飞碟</td><td>🛎️</td><td>服务铃</td><td>🧳</td><td>行李箱</td></tr><tr><td>⌛</td><td>沙漏</td><td>⏳</td><td>沙正往下流的沙漏</td><td>⌚</td><td>手表</td></tr><tr><td>⏰</td><td>闹钟</td><td>⏱️</td><td>秒表</td><td>⏲️</td><td>定时器</td></tr><tr><td>🕰️</td><td>座钟</td><td>🕛</td><td>十二点</td><td>🕧</td><td>十二点半</td></tr><tr><td>🕐</td><td>一点</td><td>🕜</td><td>一点半</td><td>🕑</td><td>两点</td></tr><tr><td>🕝</td><td>两点半</td><td>🕒</td><td>三点</td><td>🕞</td><td>三点半</td></tr><tr><td>🕓</td><td>四点</td><td>🕟</td><td>四点半</td><td>🕔</td><td>五点</td></tr><tr><td>🕠</td><td>五点半</td><td>🕕</td><td>六点</td><td>🕡</td><td>六点半</td></tr><tr><td>🕖</td><td>七点</td><td>🕢</td><td>七点半</td><td>🕗</td><td>八点</td></tr><tr><td>🕣</td><td>八点半</td><td>🕘</td><td>九点</td><td>🕤</td><td>九点半</td></tr><tr><td>🕙</td><td>十点</td><td>🕥</td><td>十点半</td><td>🕚</td><td>十一点</td></tr><tr><td>🕦</td><td>十一点半</td><td>🌑</td><td>朔月</td><td>🌒</td><td>娥眉月</td></tr><tr><td>🌓</td><td>上弦月</td><td>🌔</td><td>盈凸月</td><td>🌕</td><td>满月</td></tr><tr><td>🌖</td><td>亏凸月</td><td>🌗</td><td>下弦月</td><td>🌘</td><td>残月</td></tr><tr><td>🌙</td><td>弯月</td><td>🌚</td><td>微笑的朔月</td><td>🌛</td><td>微笑的上弦月</td></tr><tr><td>🌜</td><td>微笑的下弦月</td><td>🌡️</td><td>温度计</td><td>☀️</td><td>太阳</td></tr><tr><td>🌝</td><td>微笑的月亮</td><td>🌞</td><td>微笑的太阳</td><td>🪐</td><td>有环行星</td></tr><tr><td>⭐</td><td>星星</td><td>🌟</td><td>闪亮的星星</td><td>🌠</td><td>流星</td></tr><tr><td>🌌</td><td>银河</td><td>☁️</td><td>云</td><td>⛅</td><td>阴</td></tr><tr><td>⛈️</td><td>雷阵雨</td><td>🌤️</td><td>晴偶有云</td><td>🌥️</td><td>多云</td></tr><tr><td>🌦️</td><td>晴转雨</td><td>🌧️</td><td>下雨</td><td>🌨️</td><td>下雪</td></tr><tr><td>🌩️</td><td>打雷</td><td>🌪️</td><td>龙卷风</td><td>🌫️</td><td>雾</td></tr><tr><td>🌬️</td><td>大风</td><td>🌀</td><td>台风</td><td>🌈</td><td>彩虹</td></tr><tr><td>🌂</td><td>收起的伞</td><td>☂️</td><td>伞</td><td>☔</td><td>雨伞</td></tr><tr><td>⛱️</td><td>阳伞</td><td>⚡</td><td>高压</td><td>❄️</td><td>雪花</td></tr><tr><td>☃️</td><td>雪与雪人</td><td>⛄</td><td>雪人</td><td>☄️</td><td>彗星</td></tr><tr><td>🔥</td><td>火焰</td><td>💧</td><td>水滴</td><td>🌊</td><td>浪花</td></tr></tbody></table></div><h2 id="活动"><a href="#活动" class="headerlink" title="活动"></a>活动</h2><div class="table-container"><table><thead><tr><th>表情</th><th>名称</th><th>表情</th><th>名称</th><th>表情</th><th>名称</th></tr></thead><tbody><tr><td>🎃</td><td>南瓜灯</td><td>🎄</td><td>圣诞树</td><td>🎆</td><td>焰火</td></tr><tr><td>🎇</td><td>烟花</td><td>🧨</td><td>爆竹</td><td>✨</td><td>闪亮</td></tr><tr><td>🎈</td><td>气球</td><td>🎉</td><td>拉炮彩带</td><td>🎊</td><td>五彩纸屑球</td></tr><tr><td>🎋</td><td>七夕树</td><td>🎍</td><td>门松</td><td>🎎</td><td>日本人形</td></tr><tr><td>🎏</td><td>鲤鱼旗</td><td>🎐</td><td>风铃</td><td>🎑</td><td>赏月</td></tr><tr><td>🧧</td><td>红包</td><td>🎀</td><td>蝴蝶结</td><td>🎁</td><td>礼物</td></tr><tr><td>🎗️</td><td>提示丝带</td><td>🎟️</td><td>入场券</td><td>🎫</td><td>票</td></tr><tr><td>🎖️</td><td>军功章</td><td>🏆</td><td>奖杯</td><td>🏅</td><td>奖牌</td></tr><tr><td>🥇</td><td>金牌</td><td>🥈</td><td>银牌</td><td>🥉</td><td>铜牌</td></tr><tr><td>⚽</td><td>足球</td><td>⚾</td><td>棒球</td><td>🥎</td><td>垒球</td></tr><tr><td>🏀</td><td>篮球</td><td>🏐</td><td>排球</td><td>🏈</td><td>美式橄榄球</td></tr><tr><td>🏉</td><td>英式橄榄球</td><td>🎾</td><td>网球</td><td>🥏</td><td>飞盘</td></tr><tr><td>🎳</td><td>保龄球</td><td>🏏</td><td>板球</td><td>🏑</td><td>曲棍球</td></tr><tr><td>🏒</td><td>冰球</td><td>🥍</td><td>袋棍球</td><td>🏓</td><td>乒乓球</td></tr><tr><td>🏸</td><td>羽毛球</td><td>🥊</td><td>拳击手套</td><td>🥋</td><td>练武服</td></tr><tr><td>🥅</td><td>球门</td><td>⛳</td><td>高尔夫球洞</td><td>⛸️</td><td>滑冰</td></tr><tr><td>🎣</td><td>钓鱼竿</td><td>🤿</td><td>潜水面罩</td><td>🎽</td><td>运动背心</td></tr><tr><td>🎿</td><td>滑雪</td><td>🛷</td><td>雪橇</td><td>🥌</td><td>冰壶</td></tr><tr><td>🎯</td><td>正中靶心的飞镖</td><td>🪀</td><td>悠悠球</td><td>🪁</td><td>风筝</td></tr><tr><td>🎱</td><td>台球</td><td>🔮</td><td>水晶球</td><td>🧿</td><td>纳扎尔护身符</td></tr><tr><td>🎮</td><td>游戏手柄</td><td>🕹️</td><td>游戏操控杆</td><td>🎰</td><td>老虎机</td></tr><tr><td>🎲</td><td>骰子</td><td>🧩</td><td>拼图</td><td>🧸</td><td>泰迪熊</td></tr><tr><td>♠️</td><td>黑桃</td><td>♥️</td><td>红桃</td><td>♦️</td><td>方片</td></tr><tr><td>♣️</td><td>梅花</td><td>♟️</td><td>兵</td><td>🃏</td><td>大小王</td></tr><tr><td>🀄</td><td>红中</td><td>🎴</td><td>花札</td><td>🎭</td><td>面具</td></tr><tr><td>🖼️</td><td>带框的画</td><td>🎨</td><td>调色盘</td><td>🧵</td><td>线</td></tr><tr><td>🧶</td><td>毛线</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table></div><h2 id="物品"><a href="#物品" class="headerlink" title="物品"></a>物品</h2><div class="table-container"><table><thead><tr><th>表情</th><th>名称</th><th>表情</th><th>名称</th><th>表情</th><th>名称</th></tr></thead><tbody><tr><td>👓</td><td>眼镜</td><td>🕶️</td><td>墨镜</td><td>🥽</td><td>护目镜</td></tr><tr><td>🥼</td><td>白大褂</td><td>🦺</td><td>救生衣</td><td>👔</td><td>领带</td></tr><tr><td>👕</td><td>T恤</td><td>👖</td><td>牛仔裤</td><td>🧣</td><td>围巾</td></tr><tr><td>🧤</td><td>手套</td><td>🧥</td><td>外套</td><td>🧦</td><td>袜子</td></tr><tr><td>👗</td><td>连衣裙</td><td>👘</td><td>和服</td><td>🥻</td><td>纱丽</td></tr><tr><td>🩱</td><td>连体泳衣</td><td>🩲</td><td>三角裤</td><td>🩳</td><td>短裤</td></tr><tr><td>👙</td><td>比基尼</td><td>👚</td><td>女装</td><td>👛</td><td>钱包</td></tr><tr><td>👜</td><td>手提包</td><td>👝</td><td>手袋</td><td>🛍️</td><td>购物袋</td></tr><tr><td>🎒</td><td>书包</td><td>👞</td><td>男鞋</td><td>👟</td><td>跑鞋</td></tr><tr><td>🥾</td><td>登山鞋</td><td>🥿</td><td>平底鞋</td><td>👠</td><td>高跟鞋</td></tr><tr><td>👡</td><td>女式凉鞋</td><td>🩰</td><td>芭蕾舞鞋</td><td>👢</td><td>女靴</td></tr><tr><td>👑</td><td>皇冠</td><td>👒</td><td>女帽</td><td>🎩</td><td>礼帽</td></tr><tr><td>🎓</td><td>毕业帽</td><td>🧢</td><td>鸭舌帽</td><td>⛑️</td><td>白十字头盔</td></tr><tr><td>📿</td><td>念珠</td><td>💄</td><td>唇膏</td><td>💍</td><td>戒指</td></tr><tr><td>💎</td><td>宝石</td><td>🔇</td><td>已静音的扬声器</td><td>🔈</td><td>低音量的扬声器</td></tr><tr><td>🔉</td><td>中等音量的扬声器</td><td>🔊</td><td>高音量的扬声器</td><td>📢</td><td>喇叭</td></tr><tr><td>📣</td><td>扩音器</td><td>📯</td><td>邮号</td><td>🔔</td><td>铃铛</td></tr><tr><td>🔕</td><td>禁止响铃</td><td>🎼</td><td>乐谱</td><td>🎵</td><td>音符</td></tr><tr><td>🎶</td><td>多个音符</td><td>🎙️</td><td>录音室麦克风</td><td>🎚️</td><td>电平滑块</td></tr><tr><td>🎛️</td><td>控制旋钮</td><td>🎤</td><td>麦克风</td><td>🎧</td><td>耳机</td></tr><tr><td>📻</td><td>收音机</td><td>🎷</td><td>萨克斯管</td><td>🎸</td><td>吉他</td></tr><tr><td>🎹</td><td>音乐键盘</td><td>🎺</td><td>小号</td><td>🎻</td><td>小提琴</td></tr><tr><td>🪕</td><td>班卓琴</td><td>🥁</td><td>鼓</td><td>📱</td><td>手机</td></tr><tr><td>📲</td><td>带有箭头的手机</td><td>☎️</td><td>电话</td><td>📞</td><td>电话听筒</td></tr><tr><td>📟</td><td>寻呼机</td><td>📠</td><td>传真机</td><td>🔋</td><td>电池</td></tr><tr><td>🔌</td><td>电源插头</td><td>💻</td><td>笔记本电脑</td><td>🖥️</td><td>台式电脑</td></tr><tr><td>🖨️</td><td>打印机</td><td>⌨️</td><td>键盘</td><td>🖱️</td><td>电脑鼠标</td></tr><tr><td>🖲️</td><td>轨迹球</td><td>💽</td><td>电脑光盘</td><td>💾</td><td>软盘</td></tr><tr><td>💿</td><td>光盘</td><td>📀</td><td>DVD</td><td>🧮</td><td>算盘</td></tr><tr><td>🎥</td><td>电影摄影机</td><td>🎞️</td><td>影片帧</td><td>📽️</td><td>电影放映机</td></tr><tr><td>🎬</td><td>场记板</td><td>📺</td><td>电视机</td><td>📷</td><td>相机</td></tr><tr><td>📸</td><td>开闪光灯的相机</td><td>📹</td><td>摄像机</td><td>📼</td><td>录像带</td></tr><tr><td>🔍</td><td>左斜的放大镜</td><td>🔎</td><td>右斜的放大镜</td><td>🕯️</td><td>蜡烛</td></tr><tr><td>💡</td><td>灯泡</td><td>🔦</td><td>手电筒</td><td>🏮</td><td>红灯笼</td></tr><tr><td>🪔</td><td>印度油灯</td><td>📔</td><td>精装笔记本</td><td>📕</td><td>合上的书本</td></tr><tr><td>📖</td><td>打开的书本</td><td>📗</td><td>绿色书本</td><td>📘</td><td>蓝色书本</td></tr><tr><td>📙</td><td>橙色书本</td><td>📚</td><td>书</td><td>📓</td><td>笔记本</td></tr><tr><td>📒</td><td>账本</td><td>📃</td><td>带卷边的页面</td><td>📜</td><td>卷轴</td></tr><tr><td>📄</td><td>文件</td><td>📰</td><td>报纸</td><td>🗞️</td><td>报纸卷</td></tr><tr><td>📑</td><td>标签页</td><td>🔖</td><td>书签</td><td>🏷️</td><td>标签</td></tr><tr><td>💰</td><td>钱袋</td><td>💴</td><td>日元</td><td>💵</td><td>美元</td></tr><tr><td>💶</td><td>欧元</td><td>💷</td><td>英镑</td><td>💸</td><td>长翅膀的钱</td></tr><tr><td>💳</td><td>信用卡</td><td>🧾</td><td>收据</td><td>💹</td><td>趋势向上且带有日元符号的图表</td></tr><tr><td>💱</td><td>货币兑换</td><td>💲</td><td>美元符号</td><td>✉️</td><td>信封</td></tr><tr><td>📧</td><td>电子邮件</td><td>📨</td><td>来信</td><td>📩</td><td>收邮件</td></tr><tr><td>📤</td><td>发件箱</td><td>📥</td><td>收件箱</td><td>📦</td><td>包裹</td></tr><tr><td>📫</td><td>有待收信件</td><td>📪</td><td>无待收信件</td><td>📬</td><td>有新信件</td></tr><tr><td>📭</td><td>无新信件</td><td>📮</td><td>邮筒</td><td>🗳️</td><td>投票箱</td></tr><tr><td>✏️</td><td>铅笔</td><td>✒️</td><td>钢笔尖</td><td>🖋️</td><td>钢笔</td></tr><tr><td>🖊️</td><td>笔</td><td>🖌️</td><td>画笔</td><td>🖍️</td><td>蜡笔</td></tr><tr><td>📝</td><td>备忘录</td><td>💼</td><td>公文包</td><td>📁</td><td>文件夹</td></tr><tr><td>📂</td><td>打开的文件夹</td><td>🗂️</td><td>索引分隔文件夹</td><td>📅</td><td>日历</td></tr><tr><td>📆</td><td>手撕日历</td><td>🗒️</td><td>线圈本</td><td>🗓️</td><td>线圈日历</td></tr><tr><td>📇</td><td>卡片索引</td><td>📈</td><td>趋势向上的图表</td><td>📉</td><td>趋势向下的图表</td></tr><tr><td>📊</td><td>条形图</td><td>📋</td><td>剪贴板</td><td>📌</td><td>图钉</td></tr><tr><td>📍</td><td>圆图钉</td><td>📎</td><td>回形针</td><td>🖇️</td><td>连起来的两个回形针</td></tr><tr><td>📏</td><td>直尺</td><td>📐</td><td>三角尺</td><td>✂️</td><td>剪刀</td></tr><tr><td>🗃️</td><td>卡片盒</td><td>🗄️</td><td>文件柜</td><td>🗑️</td><td>垃圾桶</td></tr><tr><td>🔒</td><td>合上的锁</td><td>🔓</td><td>打开的锁</td><td>🔏</td><td>墨水笔和锁</td></tr><tr><td>🔐</td><td>钥匙和锁</td><td>🔑</td><td>钥匙</td><td>🗝️</td><td>老式钥匙</td></tr><tr><td>🔨</td><td>锤子</td><td>🪓</td><td>斧头</td><td>⛏️</td><td>铁镐</td></tr><tr><td>⚒️</td><td>锤子与镐</td><td>🛠️</td><td>锤子与扳手</td><td>🗡️</td><td>匕首</td></tr><tr><td>⚔️</td><td>交叉放置的剑</td><td>🔫</td><td>手枪</td><td>🏹</td><td>弓和箭</td></tr><tr><td>🛡️</td><td>盾牌</td><td>🔧</td><td>扳手</td><td>🔩</td><td>螺母与螺栓</td></tr><tr><td>⚙️</td><td>齿轮</td><td>🗜️</td><td>夹钳</td><td>⚖️</td><td>天平</td></tr><tr><td>🦯</td><td>盲杖</td><td>🔗</td><td>链接</td><td>⛓️</td><td>链条</td></tr><tr><td>🧰</td><td>工具箱</td><td>🧲</td><td>磁铁</td><td>⚗️</td><td>蒸馏器</td></tr><tr><td>🧪</td><td>试管</td><td>🧫</td><td>培养皿</td><td>🧬</td><td>DNA</td></tr><tr><td>🔬</td><td>显微镜</td><td>🔭</td><td>望远镜</td><td>📡</td><td>卫星天线</td></tr><tr><td>💉</td><td>注射器</td><td>🩸</td><td>血滴</td><td>💊</td><td>药丸</td></tr><tr><td>🩹</td><td>创可贴</td><td>🩺</td><td>听诊器</td><td>🚪</td><td>门</td></tr><tr><td>🛏️</td><td>床</td><td>🛋️</td><td>沙发和灯</td><td>🪑</td><td>椅子</td></tr><tr><td>🚽</td><td>马桶</td><td>🚿</td><td>淋浴</td><td>🛁</td><td>浴缸</td></tr><tr><td>🪒</td><td>剃须刀</td><td>🧴</td><td>乳液瓶</td><td>🧷</td><td>安全别针</td></tr><tr><td>🧹</td><td>扫帚</td><td>🧺</td><td>筐</td><td>🧻</td><td>卷纸</td></tr><tr><td>🧼</td><td>皂</td><td>🧽</td><td>海绵</td><td>🧯</td><td>灭火器</td></tr><tr><td>🛒</td><td>购物车</td><td>🚬</td><td>香烟</td><td>⚰️</td><td>棺材</td></tr><tr><td>⚱️</td><td>骨灰缸</td><td>🗿</td><td>摩埃</td><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table></div><h2 id="符号标志"><a href="#符号标志" class="headerlink" title="符号标志"></a>符号标志</h2><div class="table-container"><table><thead><tr><th>表情</th><th>名称</th><th>表情</th><th>名称</th><th>表情</th><th>名称</th></tr></thead><tbody><tr><td>🏧</td><td>取款机</td><td>🚮</td><td>倒垃圾</td><td>🚰</td><td>饮用水</td></tr><tr><td>♿</td><td>轮椅标识</td><td>🚹</td><td>男厕</td><td>🚺</td><td>女厕</td></tr><tr><td>🚻</td><td>卫生间</td><td>🚼</td><td>宝宝</td><td>🚾</td><td>厕所</td></tr><tr><td>🛂</td><td>护照检查</td><td>🛃</td><td>海关</td><td>🛄</td><td>提取行李</td></tr><tr><td>🛅</td><td>寄存行李</td><td>⚠️</td><td>警告</td><td>🚸</td><td>儿童过街</td></tr><tr><td>⛔</td><td>禁止通行</td><td>🚫</td><td>禁止</td><td>🚳</td><td>禁止自行车</td></tr><tr><td>🚭</td><td>禁止吸烟</td><td>🚯</td><td>禁止乱扔垃圾</td><td>🚱</td><td>非饮用水</td></tr><tr><td>🚷</td><td>禁止行人通行</td><td>📵</td><td>禁止使用手机</td><td>🔞</td><td>18禁</td></tr><tr><td>☢️</td><td>辐射</td><td>☣️</td><td>生物危害</td><td>⬆️</td><td>向上箭头</td></tr><tr><td>↗️</td><td>右上箭头</td><td>➡️</td><td>向右箭头</td><td>↘️</td><td>右下箭头</td></tr><tr><td>⬇️</td><td>向下箭头</td><td>↙️</td><td>左下箭头</td><td>⬅️</td><td>向左箭头</td></tr><tr><td>↖️</td><td>左上箭头</td><td>↕️</td><td>上下箭头</td><td>↔️</td><td>左右箭头</td></tr><tr><td>↩️</td><td>右转弯箭头</td><td>↪️</td><td>左转弯箭头</td><td>⤴️</td><td>右上弯箭头</td></tr><tr><td>⤵️</td><td>右下弯箭头</td><td>🔃</td><td>顺时针垂直箭头</td><td>🔄</td><td>逆时针箭头按钮</td></tr><tr><td>🔙</td><td>返回箭头</td><td>🔚</td><td>结束箭头</td><td>🔛</td><td>ON! 箭头</td></tr><tr><td>🔜</td><td>SOON 箭头</td><td>🔝</td><td>置顶</td><td>🛐</td><td>宗教场所</td></tr><tr><td>⚛️</td><td>原子符号</td><td>🕉️</td><td>奥姆</td><td>✡️</td><td>六芒星</td></tr><tr><td>☸️</td><td>法轮</td><td>☯️</td><td>阴阳</td><td>✝️</td><td>十字架</td></tr><tr><td>☦️</td><td>东正教十字架</td><td>☪️</td><td>星月</td><td>☮️</td><td>和平符号</td></tr><tr><td>🕎</td><td>烛台</td><td>🔯</td><td>带中间点的六芒星</td><td>♈</td><td>白羊座</td></tr><tr><td>♉</td><td>金牛座</td><td>♊</td><td>双子座</td><td>♋</td><td>巨蟹座</td></tr><tr><td>♌</td><td>狮子座</td><td>♍</td><td>处女座</td><td>♎</td><td>天秤座</td></tr><tr><td>♏</td><td>天蝎座</td><td>♐</td><td>射手座</td><td>♑</td><td>摩羯座</td></tr><tr><td>♒</td><td>水瓶座</td><td>♓</td><td>双鱼座</td><td>⛎</td><td>蛇夫座</td></tr><tr><td>🔀</td><td>随机播放音轨按钮</td><td>🔁</td><td>重复按钮</td><td>🔂</td><td>重复一次按钮</td></tr><tr><td>▶️</td><td>播放按钮</td><td>⏩</td><td>快进按钮</td><td>⏭️</td><td>下一个音轨按钮</td></tr><tr><td>⏯️</td><td>播放或暂停按钮</td><td>◀️</td><td>倒退按钮</td><td>⏪</td><td>快退按钮</td></tr><tr><td>⏮️</td><td>上一个音轨按钮</td><td>🔼</td><td>向上三角形按钮</td><td>⏫</td><td>快速上升按钮</td></tr><tr><td>🔽</td><td>向下三角形按钮</td><td>⏬</td><td>快速下降按钮</td><td>⏸️</td><td>暂停按钮</td></tr><tr><td>⏹️</td><td>停止按钮</td><td>⏺️</td><td>录制按钮</td><td>⏏️</td><td>推出按钮</td></tr><tr><td>🎦</td><td>电影院</td><td>🔅</td><td>低亮度按钮</td><td>🔆</td><td>高亮度按钮</td></tr><tr><td>📶</td><td>信号强度条</td><td>📳</td><td>振动模式</td><td>📴</td><td>手机关机</td></tr><tr><td>♀️</td><td>女性符号</td><td>♂️</td><td>男性符号</td><td>⚕️</td><td>医疗标志</td></tr><tr><td>♾️</td><td>无限</td><td>♻️</td><td>回收标志</td><td>⚜️</td><td>百合花饰</td></tr><tr><td>🔱</td><td>三叉戟徽章</td><td>📛</td><td>姓名牌</td><td>🔰</td><td>日本新手驾驶标志</td></tr><tr><td>⭕</td><td>红色空心圆圈</td><td>✅</td><td>勾号按钮</td><td>☑️</td><td>勾选框</td></tr><tr><td>✔️</td><td>勾号</td><td>✖️</td><td>乘号</td><td>❌</td><td>叉号</td></tr><tr><td>❎</td><td>叉号按钮</td><td>➕</td><td>加号</td><td>➖</td><td>减号</td></tr><tr><td>➗</td><td>除号</td><td>➰</td><td>卷曲环</td><td>➿</td><td>双卷曲环</td></tr><tr><td>〽️</td><td>庵点</td><td>✳️</td><td>八轮辐星号</td><td>✴️</td><td>八角星</td></tr><tr><td>❇️</td><td>火花</td><td>‼️</td><td>双感叹号</td><td>⁉️</td><td>感叹疑问号</td></tr><tr><td>❓</td><td>问号</td><td>❔</td><td>白色问号</td><td>❕</td><td>白色感叹号</td></tr><tr><td>❗</td><td>感叹号</td><td>〰️</td><td>波浪型破折号</td><td>©️</td><td>版权</td></tr><tr><td>®️</td><td>注册</td><td>™️</td><td>商标</td><td>#️⃣</td><td>按键: #</td></tr><tr><td>*️⃣</td><td>按键: *</td><td>0️⃣</td><td>按键: 0</td><td>1️⃣</td><td>按键: 1</td></tr><tr><td>2️⃣</td><td>按键: 2</td><td>3️⃣</td><td>按键: 3</td><td>4️⃣</td><td>按键: 4</td></tr><tr><td>5️⃣</td><td>按键: 5</td><td>6️⃣</td><td>按键: 6</td><td>7️⃣</td><td>按键: 7</td></tr><tr><td>8️⃣</td><td>按键: 8</td><td>9️⃣</td><td>按键: 9</td><td>🔟</td><td>按键: 10</td></tr><tr><td>🔠</td><td>输入大写拉丁字母</td><td>🔡</td><td>输入小写拉丁字母</td><td>🔢</td><td>输入数字</td></tr><tr><td>🔣</td><td>输入符号</td><td>🔤</td><td>输入拉丁字母</td><td>🅰️</td><td>A 型血</td></tr><tr><td>🆎</td><td>AB 型血</td><td>🅱️</td><td>B 型血</td><td>🆑</td><td>CL按钮</td></tr><tr><td>🆒</td><td>cool按钮</td><td>🆓</td><td>免费按钮</td><td>ℹ️</td><td>信息</td></tr><tr><td>🆔</td><td>ID按钮</td><td>Ⓜ️</td><td>圆圈包围的M</td><td>🆕</td><td>new按钮</td></tr><tr><td>🆖</td><td>NG按钮</td><td>🅾️</td><td>O 型血</td><td>🆗</td><td>OK按钮</td></tr><tr><td>🅿️</td><td>停车按钮</td><td>🆘</td><td>SOS按钮</td><td>🆙</td><td>up按钮</td></tr><tr><td>🆚</td><td>VS按钮</td><td>🈁</td><td>日文的“这里”按钮</td><td>🈂️</td><td>日文的“服务费”按钮</td></tr><tr><td>🈷️</td><td>日文的“月总量”按钮</td><td>🈶</td><td>日文的“收费”按钮</td><td>🈯</td><td>日文的“预留”按钮</td></tr><tr><td>🉐</td><td>日文的“议价”按钮</td><td>🈹</td><td>日文的“打折”按钮</td><td>🈚</td><td>日文的“免费”按钮</td></tr><tr><td>🈲</td><td>日文的“禁止”按钮</td><td>🉑</td><td>日文的“可接受”按钮</td><td>🈸</td><td>日文的“申请”按钮</td></tr><tr><td>🈴</td><td>日文的“合格”按钮</td><td>🈳</td><td>日文的“有空位”按钮</td><td>㊗️</td><td>日文的“祝贺”按钮</td></tr><tr><td>㊙️</td><td>日文的“秘密”按钮</td><td>🈺</td><td>日文的“开始营业”按钮</td><td>🈵</td><td>日文的“没有空位”按钮</td></tr><tr><td>🔴</td><td>红色圆</td><td>🟠</td><td>橙色圆</td><td>🟡</td><td>黄色圆</td></tr><tr><td>🟢</td><td>绿色圆</td><td>🔵</td><td>蓝色圆</td><td>🟣</td><td>紫色圆</td></tr><tr><td>🟤</td><td>棕色圆</td><td>⚫</td><td>黑色圆</td><td>⚪</td><td>白色圆</td></tr><tr><td>🟥</td><td>红色方块</td><td>🟧</td><td>橙色方块</td><td>🟨</td><td>黄色方块</td></tr><tr><td>🟩</td><td>绿色方块</td><td>🟦</td><td>蓝色方块</td><td>🟪</td><td>紫色方块</td></tr><tr><td>🟫</td><td>棕色方块</td><td>⬛</td><td>黑线大方框</td><td>⬜</td><td>白线大方框</td></tr><tr><td>◼️</td><td>黑色中方块</td><td>◻️</td><td>白色中方块</td><td>◾</td><td>黑色中小方块</td></tr><tr><td>◽</td><td>白色中小方块</td><td>▪️</td><td>黑色小方块</td><td>▫️</td><td>白色小方块</td></tr><tr><td>🔶</td><td>橙色大菱形</td><td>🔷</td><td>蓝色大菱形</td><td>🔸</td><td>橙色小菱形</td></tr><tr><td>🔹</td><td>蓝色小菱形</td><td>🔺</td><td>红色正三角</td><td>🔻</td><td>红色倒三角</td></tr><tr><td>💠</td><td>带圆点的菱形</td><td>🔘</td><td>单选按钮</td><td>🔳</td><td>白色方形按钮</td></tr><tr><td>🔲</td><td>黑色方形按钮</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table></div><h2 id="旗帜"><a href="#旗帜" class="headerlink" title="旗帜"></a>旗帜</h2><div class="table-container"><table><thead><tr><th>表情</th><th>名称</th><th>表情</th><th>名称</th><th>表情</th><th>名称</th></tr></thead><tbody><tr><td>🏁</td><td>终点旗</td><td>🚩</td><td>三角旗</td><td>🎌</td><td>交叉旗</td></tr><tr><td>🏴</td><td>举黑旗</td><td>🏳️</td><td>举白旗</td><td>🏳️‍🌈</td><td>彩虹旗</td></tr><tr><td>🏴‍☠️</td><td>海盗旗</td><td>🇦🇨</td><td>旗: 阿森松岛</td><td>🇦🇩</td><td>旗: 安道尔</td></tr><tr><td>🇦🇪</td><td>旗: 阿拉伯联合酋长国</td><td>🇦🇫</td><td>旗: 阿富汗</td><td>🇦🇬</td><td>旗: 安提瓜和巴布达</td></tr><tr><td>🇦🇮</td><td>旗: 安圭拉</td><td>🇦🇱</td><td>旗: 阿尔巴尼亚</td><td>🇦🇲</td><td>旗: 亚美尼亚</td></tr><tr><td>🇦🇴</td><td>旗: 安哥拉</td><td>🇦🇶</td><td>旗: 南极洲</td><td>🇦🇷</td><td>旗: 阿根廷</td></tr><tr><td>🇦🇸</td><td>旗: 美属萨摩亚</td><td>🇦🇹</td><td>旗: 奥地利</td><td>🇦🇺</td><td>旗: 澳大利亚</td></tr><tr><td>🇦🇼</td><td>旗: 阿鲁巴</td><td>🇦🇽</td><td>旗: 奥兰群岛</td><td>🇦🇿</td><td>旗: 阿塞拜疆</td></tr><tr><td>🇧🇦</td><td>旗: 波斯尼亚和黑塞哥维那</td><td>🇧🇧</td><td>旗: 巴巴多斯</td><td>🇧🇩</td><td>旗: 孟加拉国</td></tr><tr><td>🇧🇪</td><td>旗: 比利时</td><td>🇧🇫</td><td>旗: 布基纳法索</td><td>🇧🇬</td><td>旗: 保加利亚</td></tr><tr><td>🇧🇭</td><td>旗: 巴林</td><td>🇧🇮</td><td>旗: 布隆迪</td><td>🇧🇯</td><td>旗: 贝宁</td></tr><tr><td>🇧🇱</td><td>旗: 圣巴泰勒米</td><td>🇧🇲</td><td>旗: 百慕大</td><td>🇧🇳</td><td>旗: 文莱</td></tr><tr><td>🇧🇴</td><td>旗: 玻利维亚</td><td>🇧🇶</td><td>旗: 荷属加勒比区</td><td>🇧🇷</td><td>旗: 巴西</td></tr><tr><td>🇧🇸</td><td>旗: 巴哈马</td><td>🇧🇹</td><td>旗: 不丹</td><td>🇧🇻</td><td>旗: 布韦岛</td></tr><tr><td>🇧🇼</td><td>旗: 博茨瓦纳</td><td>🇧🇾</td><td>旗: 白俄罗斯</td><td>🇧🇿</td><td>旗: 伯利兹</td></tr><tr><td>🇨🇦</td><td>旗: 加拿大</td><td>🇨🇨</td><td>旗: 科科斯（基林）群岛</td><td>🇨🇩</td><td>旗: 刚果（金）</td></tr><tr><td>🇨🇫</td><td>旗: 中非共和国</td><td>🇨🇬</td><td>旗: 刚果（布）</td><td>🇨🇭</td><td>旗: 瑞士</td></tr><tr><td>🇨🇮</td><td>旗: 科特迪瓦</td><td>🇨🇰</td><td>旗: 库克群岛</td><td>🇨🇱</td><td>旗: 智利</td></tr><tr><td>🇨🇲</td><td>旗: 喀麦隆</td><td>🇨🇳</td><td>旗: 中国</td><td>🇨🇴</td><td>旗: 哥伦比亚</td></tr><tr><td>🇨🇵</td><td>旗: 克利珀顿岛</td><td>🇨🇷</td><td>旗: 哥斯达黎加</td><td>🇨🇺</td><td>旗: 古巴</td></tr><tr><td>🇨🇻</td><td>旗: 佛得角</td><td>🇨🇼</td><td>旗: 库拉索</td><td>🇨🇽</td><td>旗: 圣诞岛</td></tr><tr><td>🇨🇾</td><td>旗: 塞浦路斯</td><td>🇨🇿</td><td>旗: 捷克</td><td>🇩🇪</td><td>旗: 德国</td></tr><tr><td>🇩🇬</td><td>旗: 迪戈加西亚岛</td><td>🇩🇯</td><td>旗: 吉布提</td><td>🇩🇰</td><td>旗: 丹麦</td></tr><tr><td>🇩🇲</td><td>旗: 多米尼克</td><td>🇩🇴</td><td>旗: 多米尼加共和国</td><td>🇩🇿</td><td>旗: 阿尔及利亚</td></tr><tr><td>🇪🇦</td><td>旗: 休达及梅利利亚</td><td>🇪🇨</td><td>旗: 厄瓜多尔</td><td>🇪🇪</td><td>旗: 爱沙尼亚</td></tr><tr><td>🇪🇬</td><td>旗: 埃及</td><td>🇪🇭</td><td>旗: 西撒哈拉</td><td>🇪🇷</td><td>旗: 厄立特里亚</td></tr><tr><td>🇪🇸</td><td>旗: 西班牙</td><td>🇪🇹</td><td>旗: 埃塞俄比亚</td><td>🇪🇺</td><td>旗: 欧盟</td></tr><tr><td>🇫🇮</td><td>旗: 芬兰</td><td>🇫🇯</td><td>旗: 斐济</td><td>🇫🇰</td><td>旗: 福克兰群岛</td></tr><tr><td>🇫🇲</td><td>旗: 密克罗尼西亚</td><td>🇫🇴</td><td>旗: 法罗群岛</td><td>🇫🇷</td><td>旗: 法国</td></tr><tr><td>🇬🇦</td><td>旗: 加蓬</td><td>🇬🇧</td><td>旗: 英国</td><td>🇬🇩</td><td>旗: 格林纳达</td></tr><tr><td>🇬🇪</td><td>旗: 格鲁吉亚</td><td>🇬🇫</td><td>旗: 法属圭亚那</td><td>🇬🇬</td><td>旗: 根西岛</td></tr><tr><td>🇬🇭</td><td>旗: 加纳</td><td>🇬🇮</td><td>旗: 直布罗陀</td><td>🇬🇱</td><td>旗: 格陵兰</td></tr><tr><td>🇬🇲</td><td>旗: 冈比亚</td><td>🇬🇳</td><td>旗: 几内亚</td><td>🇬🇵</td><td>旗: 瓜德罗普</td></tr><tr><td>🇬🇶</td><td>旗: 赤道几内亚</td><td>🇬🇷</td><td>旗: 希腊</td><td>🇬🇸</td><td>旗: 南乔治亚和南桑威奇群岛</td></tr><tr><td>🇬🇹</td><td>旗: 危地马拉</td><td>🇬🇺</td><td>旗: 关岛</td><td>🇬🇼</td><td>旗: 几内亚比绍</td></tr><tr><td>🇬🇾</td><td>旗: 圭亚那</td><td>🇭🇰</td><td>旗: 中国香港特别行政区</td><td>🇭🇲</td><td>旗: 赫德岛和麦克唐纳群岛</td></tr><tr><td>🇭🇳</td><td>旗: 洪都拉斯</td><td>🇭🇷</td><td>旗: 克罗地亚</td><td>🇭🇹</td><td>旗: 海地</td></tr><tr><td>🇭🇺</td><td>旗: 匈牙利</td><td>🇮🇨</td><td>旗: 加纳利群岛</td><td>🇮🇩</td><td>旗: 印度尼西亚</td></tr><tr><td>🇮🇪</td><td>旗: 爱尔兰</td><td>🇮🇱</td><td>旗: 以色列</td><td>🇮🇲</td><td>旗: 马恩岛</td></tr><tr><td>🇮🇳</td><td>旗: 印度</td><td>🇮🇴</td><td>旗: 英属印度洋领地</td><td>🇮🇶</td><td>旗: 伊拉克</td></tr><tr><td>🇮🇷</td><td>旗: 伊朗</td><td>🇮🇸</td><td>旗: 冰岛</td><td>🇮🇹</td><td>旗: 意大利</td></tr><tr><td>🇯🇪</td><td>旗: 泽西岛</td><td>🇯🇲</td><td>旗: 牙买加</td><td>🇯🇴</td><td>旗: 约旦</td></tr><tr><td>🇯🇵</td><td>旗: 日本</td><td>🇰🇪</td><td>旗: 肯尼亚</td><td>🇰🇬</td><td>旗: 吉尔吉斯斯坦</td></tr><tr><td>🇰🇭</td><td>旗: 柬埔寨</td><td>🇰🇮</td><td>旗: 基里巴斯</td><td>🇰🇲</td><td>旗: 科摩罗</td></tr><tr><td>🇰🇳</td><td>旗: 圣基茨和尼维斯</td><td>🇰🇵</td><td>旗: 朝鲜</td><td>🇰🇷</td><td>旗: 韩国</td></tr><tr><td>🇰🇼</td><td>旗: 科威特</td><td>🇰🇾</td><td>旗: 开曼群岛</td><td>🇰🇿</td><td>旗: 哈萨克斯坦</td></tr><tr><td>🇱🇦</td><td>旗: 老挝</td><td>🇱🇧</td><td>旗: 黎巴嫩</td><td>🇱🇨</td><td>旗: 圣卢西亚</td></tr><tr><td>🇱🇮</td><td>旗: 列支敦士登</td><td>🇱🇰</td><td>旗: 斯里兰卡</td><td>🇱🇷</td><td>旗: 利比里亚</td></tr><tr><td>🇱🇸</td><td>旗: 莱索托</td><td>🇱🇹</td><td>旗: 立陶宛</td><td>🇱🇺</td><td>旗: 卢森堡</td></tr><tr><td>🇱🇻</td><td>旗: 拉脱维亚</td><td>🇱🇾</td><td>旗: 利比亚</td><td>🇲🇦</td><td>旗: 摩洛哥</td></tr><tr><td>🇲🇨</td><td>旗: 摩纳哥</td><td>🇲🇩</td><td>旗: 摩尔多瓦</td><td>🇲🇪</td><td>旗: 黑山</td></tr><tr><td>🇲🇫</td><td>旗: 法属圣马丁</td><td>🇲🇬</td><td>旗: 马达加斯加</td><td>🇲🇭</td><td>旗: 马绍尔群岛</td></tr><tr><td>🇲🇰</td><td>旗: 北马其顿</td><td>🇲🇱</td><td>旗: 马里</td><td>🇲🇲</td><td>旗: 缅甸</td></tr><tr><td>🇲🇳</td><td>旗: 蒙古</td><td>🇲🇴</td><td>旗: 中国澳门特别行政区</td><td>🇲🇵</td><td>旗: 北马里亚纳群岛</td></tr><tr><td>🇲🇶</td><td>旗: 马提尼克</td><td>🇲🇷</td><td>旗: 毛里塔尼亚</td><td>🇲🇸</td><td>旗: 蒙特塞拉特</td></tr><tr><td>🇲🇹</td><td>旗: 马耳他</td><td>🇲🇺</td><td>旗: 毛里求斯</td><td>🇲🇻</td><td>旗: 马尔代夫</td></tr><tr><td>🇲🇼</td><td>旗: 马拉维</td><td>🇲🇽</td><td>旗: 墨西哥</td><td>🇲🇾</td><td>旗: 马来西亚</td></tr><tr><td>🇲🇿</td><td>旗: 莫桑比克</td><td>🇳🇦</td><td>旗: 纳米比亚</td><td>🇳🇨</td><td>旗: 新喀里多尼亚</td></tr><tr><td>🇳🇪</td><td>旗: 尼日尔</td><td>🇳🇫</td><td>旗: 诺福克岛</td><td>🇳🇬</td><td>旗: 尼日利亚</td></tr><tr><td>🇳🇮</td><td>旗: 尼加拉瓜</td><td>🇳🇱</td><td>旗: 荷兰</td><td>🇳🇴</td><td>旗: 挪威</td></tr><tr><td>🇳🇵</td><td>旗: 尼泊尔</td><td>🇳🇷</td><td>旗: 瑙鲁</td><td>🇳🇺</td><td>旗: 纽埃</td></tr><tr><td>🇳🇿</td><td>旗: 新西兰</td><td>🇴🇲</td><td>旗: 阿曼</td><td>🇵🇦</td><td>旗: 巴拿马</td></tr><tr><td>🇵🇪</td><td>旗: 秘鲁</td><td>🇵🇫</td><td>旗: 法属波利尼西亚</td><td>🇵🇬</td><td>旗: 巴布亚新几内亚</td></tr><tr><td>🇵🇭</td><td>旗: 菲律宾</td><td>🇵🇰</td><td>旗: 巴基斯坦</td><td>🇵🇱</td><td>旗: 波兰</td></tr><tr><td>🇵🇲</td><td>旗: 圣皮埃尔和密克隆群岛</td><td>🇵🇳</td><td>旗: 皮特凯恩群岛</td><td>🇵🇷</td><td>旗: 波多黎各</td></tr><tr><td>🇵🇸</td><td>旗: 巴勒斯坦领土</td><td>🇵🇹</td><td>旗: 葡萄牙</td><td>🇵🇼</td><td>旗: 帕劳</td></tr><tr><td>🇵🇾</td><td>旗: 巴拉圭</td><td>🇶🇦</td><td>旗: 卡塔尔</td><td>🇷🇪</td><td>旗: 留尼汪</td></tr><tr><td>🇷🇴</td><td>旗: 罗马尼亚</td><td>🇷🇸</td><td>旗: 塞尔维亚</td><td>🇷🇺</td><td>旗: 俄罗斯</td></tr><tr><td>🇷🇼</td><td>旗: 卢旺达</td><td>🇸🇦</td><td>旗: 沙特阿拉伯</td><td>🇸🇧</td><td>旗: 所罗门群岛</td></tr><tr><td>🇸🇨</td><td>旗: 塞舌尔</td><td>🇸🇩</td><td>旗: 苏丹</td><td>🇸🇪</td><td>旗: 瑞典</td></tr><tr><td>🇸🇬</td><td>旗: 新加坡</td><td>🇸🇭</td><td>旗: 圣赫勒拿</td><td>🇸🇮</td><td>旗: 斯洛文尼亚</td></tr><tr><td>🇸🇯</td><td>旗: 斯瓦尔巴和扬马延</td><td>🇸🇰</td><td>旗: 斯洛伐克</td><td>🇸🇱</td><td>旗: 塞拉利昂</td></tr><tr><td>🇸🇲</td><td>旗: 圣马力诺</td><td>🇸🇳</td><td>旗: 塞内加尔</td><td>🇸🇴</td><td>旗: 索马里</td></tr><tr><td>🇸🇷</td><td>旗: 苏里南</td><td>🇸🇸</td><td>旗: 南苏丹</td><td>🇸🇹</td><td>旗: 圣多美和普林西比</td></tr><tr><td>🇸🇻</td><td>旗: 萨尔瓦多</td><td>🇸🇽</td><td>旗: 荷属圣马丁</td><td>🇸🇾</td><td>旗: 叙利亚</td></tr><tr><td>🇸🇿</td><td>旗: 斯威士兰</td><td>🇹🇦</td><td>旗: 特里斯坦-达库尼亚群岛</td><td>🇹🇨</td><td>旗: 特克斯和凯科斯群岛</td></tr><tr><td>🇹🇩</td><td>旗: 乍得</td><td>🇹🇫</td><td>旗: 法属南部领地</td><td>🇹🇬</td><td>旗: 多哥</td></tr><tr><td>🇹🇭</td><td>旗: 泰国</td><td>🇹🇯</td><td>旗: 塔吉克斯坦</td><td>🇹🇰</td><td>旗: 托克劳</td></tr><tr><td>🇹🇱</td><td>旗: 东帝汶</td><td>🇹🇲</td><td>旗: 土库曼斯坦</td><td>🇹🇳</td><td>旗: 突尼斯</td></tr><tr><td>🇹🇴</td><td>旗: 汤加</td><td>🇹🇷</td><td>旗: 土耳其</td><td>🇹🇹</td><td>旗: 特立尼达和多巴哥</td></tr><tr><td>🇹🇻</td><td>旗: 图瓦卢</td><td>🇹🇼</td><td>旗: 台湾</td><td>🇹🇿</td><td>旗: 坦桑尼亚</td></tr><tr><td>🇺🇦</td><td>旗: 乌克兰</td><td>🇺🇬</td><td>旗: 乌干达</td><td>🇺🇲</td><td>旗: 美国本土外小岛屿</td></tr><tr><td>🇺🇳</td><td>旗: 联合国</td><td>🇺🇸</td><td>旗: 美国</td><td>🇺🇾</td><td>旗: 乌拉圭</td></tr><tr><td>🇺🇿</td><td>旗: 乌兹别克斯坦</td><td>🇻🇦</td><td>旗: 梵蒂冈</td><td>🇻🇨</td><td>旗: 圣文森特和格林纳丁斯</td></tr><tr><td>🇻🇪</td><td>旗: 委内瑞拉</td><td>🇻🇬</td><td>旗: 英属维尔京群岛</td><td>🇻🇮</td><td>旗: 美属维尔京群岛</td></tr><tr><td>🇻🇳</td><td>旗: 越南</td><td>🇻🇺</td><td>旗: 瓦努阿图</td><td>🇼🇫</td><td>旗: 瓦利斯和富图纳</td></tr><tr><td>🇼🇸</td><td>旗: 萨摩亚</td><td>🇽🇰</td><td>旗: 科索沃</td><td>🇾🇪</td><td>旗: 也门</td></tr><tr><td>🇾🇹</td><td>旗: 马约特</td><td>🇿🇦</td><td>旗: 南非</td><td>🇿🇲</td><td>旗: 赞比亚</td></tr><tr><td>🇿🇼</td><td>旗: 津巴布韦</td><td>🏴󠁧󠁢󠁥󠁮󠁧󠁿</td><td>旗: 英格兰</td><td>🏴󠁧󠁢󠁳󠁣󠁴󠁿</td><td>旗: 苏格兰</td></tr><tr><td>🏴󠁧󠁢󠁷󠁬󠁳󠁿</td><td>旗: 威尔士</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table></div><p><style>  table td:nth-child(odd) {    font-size: 1.2rem;    text-align: center;  }</style></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%88%86%E4%BA%AB%E8%BD%AC%E8%BD%BD/">分享转载</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E6%96%87%E6%A1%A3/">文档</category>
      
      
      <comments>https://blog.eurkon.com/post/f349e1f0.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Python 自建 API 合集</title>
      <link>https://blog.eurkon.com/post/ee499a0b.html</link>
      <guid>https://blog.eurkon.com/post/ee499a0b.html</guid>
      <pubDate>Mon, 07 Jun 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文收集了常用的 API 接口以及自己部署于 Vercel 的 Python API 合集。&lt;/p&gt;
&lt;p&gt;&lt;a class=&quot;ghcard</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文收集了常用的 API 接口以及自己部署于 Vercel 的 Python API 合集。</p><p><a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/Eurkon/api"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://github-readme-stats.eurkon.com/api/pin/?username=Eurkon&repo=api&show_owner=true"></a></p><h2 id="自建-API"><a href="#自建-API" class="headerlink" title="自建 API"></a>自建 API</h2><h3 id="百度"><a href="#百度" class="headerlink" title="百度"></a>百度</h3><h4 id="百度统计"><a href="#百度统计" class="headerlink" title="百度统计"></a>百度统计</h4><p><strong>接口地址：</strong> /baidu/tongji</p><p><strong>描述：</strong> 重定向请求百度统计，解决跨域问题</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong> <a href="https://tongji.baidu.com/api/manual/">百度统计用户手册</a></p><p><strong>请求示例：</strong> 无</p><h4 id="百度翻译"><a href="#百度翻译" class="headerlink" title="百度翻译"></a>百度翻译</h4><p><strong>接口地址：</strong> /baidu/translate</p><p><strong>描述：</strong> 百度翻译</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong></p><div class="table-container"><table><thead><tr><th>字段名</th><th>字段说明</th><th>字段类型</th><th>默认值</th><th>是否必填</th></tr></thead><tbody><tr><td>fr</td><td>源语言</td><td>string</td><td>英语</td><td>否</td></tr><tr><td>to</td><td>翻译语言</td><td>string</td><td>中文</td><td>否</td></tr><tr><td>content</td><td>源语言</td><td>string</td><td>Hello World</td><td>否</td></tr></tbody></table></div><p><strong>请求示例：</strong> <a href="https://api.eurkon.com/baidu/translate?fr=英语&amp;to=中文&amp;content=helloworld">https://api.eurkon.com/baidu/translate?fr=英语&amp;to=中文&amp;content=helloworld</a></p><h3 id="谷歌"><a href="#谷歌" class="headerlink" title="谷歌"></a>谷歌</h3><h4 id="谷歌翻译"><a href="#谷歌翻译" class="headerlink" title="谷歌翻译"></a>谷歌翻译</h4><p><strong>接口地址：</strong> /google/translate</p><p><strong>描述：</strong> 谷歌翻译</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong></p><div class="table-container"><table><thead><tr><th>字段名</th><th>字段说明</th><th>字段类型</th><th>默认值</th><th>是否必填</th></tr></thead><tbody><tr><td>fr</td><td>源语言</td><td>string</td><td>英语</td><td>否</td></tr><tr><td>to</td><td>翻译语言</td><td>string</td><td>中文</td><td>否</td></tr><tr><td>content</td><td>翻译内容</td><td>string</td><td>Hello World</td><td>否</td></tr></tbody></table></div><p><strong>请求示例：</strong> <a href="https://api.eurkon.com/google/translate?fr=英语&amp;to=中文&amp;content=helloworld">https://api.eurkon.com/google/translate?fr=英语&amp;to=中文&amp;content=helloworld</a></p><h3 id="微博"><a href="#微博" class="headerlink" title="微博"></a>微博</h3><h4 id="微博热搜"><a href="#微博热搜" class="headerlink" title="微博热搜"></a>微博热搜</h4><p><strong>接口地址：</strong> /weibo/top</p><p><strong>描述：</strong> 爬取微博热搜</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong> 无</p><p><strong>请求示例：</strong> <a href="https://api.eurkon.com/weibo/top">https://api.eurkon.com/weibo/top</a></p><h3 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h3><h4 id="生成二维码"><a href="#生成二维码" class="headerlink" title="生成二维码"></a>生成二维码</h4><p><strong>接口地址：</strong> /tools/qrcode</p><p><strong>描述：</strong> 重定向请求百度统计，解决跨域问题</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong></p><div class="table-container"><table><thead><tr><th>字段名</th><th>字段说明</th><th>字段类型</th><th>默认值</th><th>是否必填</th></tr></thead><tbody><tr><td>content</td><td>二维码内容</td><td>string</td><td>Hello World</td><td>否</td></tr></tbody></table></div><p><strong>请求示例：</strong> <a href="https://api.eurkon.com/api?api=tools_qrcode&amp;content=HelloWorld">https://api.eurkon.com/api?api=tools_qrcode&amp;content=HelloWorld</a></p><h3 id="有道"><a href="#有道" class="headerlink" title="有道"></a>有道</h3><h4 id="有道翻译"><a href="#有道翻译" class="headerlink" title="有道翻译"></a>有道翻译</h4><p><strong>接口地址：</strong> /youdao/translate</p><p><strong>描述：</strong> 有道翻译</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong></p><div class="table-container"><table><thead><tr><th>字段名</th><th>字段说明</th><th>字段类型</th><th>默认值</th><th>是否必填</th></tr></thead><tbody><tr><td>content</td><td>翻译内容</td><td>string</td><td>Hello World</td><td>否</td></tr></tbody></table></div><p><strong>请求示例：</strong> <a href="https://api.eurkon.com/youdao/translate?content=helloworld">https://api.eurkon.com/youdao/translate?content=helloworld</a></p><h2 id="常用-API"><a href="#常用-API" class="headerlink" title="常用 API"></a>常用 API</h2><h3 id="百度百科历史今日"><a href="#百度百科历史今日" class="headerlink" title="百度百科历史今日"></a>百度百科历史今日</h3><p><strong>接口地址：</strong> <a href="https://baike.baidu.com/cms/home/eventsOnHistory/">https://baike.baidu.com/cms/home/eventsOnHistory/</a></p><p><strong>描述：</strong> 百度百科历史今日</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong></p><div class="table-container"><table><thead><tr><th>字段名</th><th>字段说明</th><th>字段类型</th><th>默认值</th><th>是否必填</th></tr></thead><tbody><tr><td>month</td><td>月份</td><td>string</td><td>无</td><td>是</td></tr></tbody></table></div><p><strong>请求示例：</strong> <a href="https://baike.baidu.com/cms/home/eventsOnHistory/01.json">https://baike.baidu.com/cms/home/eventsOnHistory/01.json</a></p><h3 id="IP、行政区编码、地址"><a href="#IP、行政区编码、地址" class="headerlink" title="IP、行政区编码、地址"></a>IP、行政区编码、地址</h3><p><strong>接口地址：</strong> <a href="https://pv.sohu.com/cityjson">https://pv.sohu.com/cityjson</a></p><p><strong>描述：</strong> 获取当前 IP 地址信息</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong> 无</p><p><strong>请求示例：</strong> <a href="https://pv.sohu.com/cityjson?ie=utf-8">https://pv.sohu.com/cityjson?ie=utf-8</a></p><h3 id="地区、国家、天气、温度、湿度"><a href="#地区、国家、天气、温度、湿度" class="headerlink" title="地区、国家、天气、温度、湿度"></a>地区、国家、天气、温度、湿度</h3><p><strong>接口地址：</strong> <a href="https://wttr.in/ip">https://wttr.in/ip</a></p><p><strong>描述：</strong> 获取当前 IP 地址和天气信息</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong></p><div class="table-container"><table><thead><tr><th>字段名</th><th>字段说明</th><th>字段类型</th><th>默认值</th><th>是否必填</th></tr></thead><tbody><tr><td>format</td><td>返回格式</td><td>string</td><td>无</td><td>否</td></tr></tbody></table></div><p><strong>请求示例：</strong> <a href="https://wttr.in/ip?format=&quot;%l+\\+%c+\\+%t+\\+%h">https://wttr.in/ip?format=&quot;%l+\\+%c+\\+%t+\\+%h</a>&quot;</p><h3 id="腾讯天气接口"><a href="#腾讯天气接口" class="headerlink" title="腾讯天气接口"></a>腾讯天气接口</h3><p><strong>接口地址：</strong> <a href="https://wis.qq.com/weather/common">https://wis.qq.com/weather/common</a></p><p><strong>描述：</strong> 获取当前 IP 地址和天气信息</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong></p><div class="table-container"><table><thead><tr><th>字段名</th><th>字段说明</th><th>字段类型</th><th>默认值</th><th>是否必填</th></tr></thead><tbody><tr><td>source</td><td>请求类型 pc/wx</td><td>string</td><td>无</td><td>是</td></tr><tr><td>weather_type</td><td>查询类型，多个用 &#124; 分隔<br>observe（当前天气）<br>forecast_1h<br>forecast_24h<br> index 穿衣，舒适度等<br>alarm（预警）<br>tips（天气介绍）<br>air（空气质量）<br>rise（日出）</td><td>string</td><td>无</td><td>是</td></tr><tr><td>province</td><td>省份</td><td>string</td><td>无</td><td>是</td></tr><tr><td>city</td><td>城市</td><td>string</td><td>无</td><td>是</td></tr><tr><td>county</td><td>县区</td><td>string</td><td>无</td><td>否</td></tr><tr><td>callback</td><td>回调函数，不传直接返回 json</td><td>string</td><td>无</td><td>否</td></tr></tbody></table></div><p><strong>请求示例：</strong> <a href="https://wis.qq.com/weather/common?source=xw&amp;weather_type=forecast_1h|forecast_24h|index|alarm|limit|tips&amp;province=广东&amp;city=广州&amp;county=天河">https://wis.qq.com/weather/common?source=xw&amp;weather_type=forecast_1h|forecast_24h|index|alarm|limit|tips&amp;province=广东&amp;city=广州&amp;county=天河</a></p><h3 id="豆瓣电影接口"><a href="#豆瓣电影接口" class="headerlink" title="豆瓣电影接口"></a>豆瓣电影接口</h3><p><strong>接口地址：</strong> <a href="https://movie.douban.com/j/search_subjects">https://movie.douban.com/j/search_subjects</a></p><p><strong>描述：</strong> 获取当前 IP 地址和天气信息</p><p><strong>请求方式：</strong> GET</p><p><strong>请求参数说明：</strong></p><p>参考页面：<a href="https://movie.douban.com/explore">https://movie.douban.com/explore</a></p><div class="table-container"><table><thead><tr><th>字段名</th><th>字段说明</th><th>字段类型</th><th>默认值</th><th>是否必填</th></tr></thead><tbody><tr><td>tag</td><td>标签</td><td>string</td><td>无</td><td>否</td></tr><tr><td>type</td><td>类型</td><td>string</td><td>movie</td><td>否</td></tr><tr><td>sort</td><td>排序</td><td>string</td><td>recommend</td><td>否</td></tr><tr><td>page_limit</td><td>返回个数</td><td>integer</td><td>20</td><td>否</td></tr><tr><td>page_start</td><td>开始索引</td><td>integer</td><td>0</td><td>否</td></tr></tbody></table></div><p><strong>请求示例：</strong> <a href="https://movie.douban.com/j/search_subjects?type=movie&amp;tag=热门&amp;sort=recommend&amp;page_limit=20&amp;page_start=0">https://movie.douban.com/j/search_subjects?type=movie&amp;tag=热门&amp;sort=recommend&amp;page_limit=20&amp;page_start=0</a></p><h2 id="常用-API-网站"><a href="#常用-API-网站" class="headerlink" title="常用 API 网站"></a>常用 API 网站</h2><h3 id="博天-API"><a href="#博天-API" class="headerlink" title="博天 API"></a><a href="https://api.btstu.cn/">博天 API</a></h3><ul><li><strong>随机壁纸</strong>：随机输出各类壁纸</li><li><strong>毒鸡汤</strong>：随机输出毒鸡汤</li><li><strong>随机头像</strong>：随机输出各类头像</li><li><strong>获取 QQ 昵称和头像</strong>：获取 QQ 昵称和头像</li><li><strong>ICP 备案查询</strong>：在线查询网站 ICP 备案</li><li><strong>QQ 信息查询</strong>：查询 QQ 的信息</li><li><strong>QQ 域名报毒检测</strong>：检测域名在 QQ 是否报毒</li><li><strong>二维码解码</strong>：解析二维码图片</li><li><strong>Qrcode 二维码</strong>：生成在线二维码</li><li><strong>域名注册查询</strong>：查询域名是否已被注册</li><li><strong>搜狗收录量</strong>：查询搜狗收录数量</li><li><strong>在线 ping</strong>：在线 ping 网站</li><li><strong>百度收录量</strong>：查询域名百度收录总量</li><li><strong>IP 签名档</strong>：输出精美 IP 信息图</li><li><strong>抖音网址检测</strong>：检测网址是否可以在抖音直接打开</li><li><strong>百度收录判断</strong>：判断网址是否已被百度收录</li><li><strong>QQ 强制聊天</strong>：无需添加 QQ 好友，直接进入聊天</li><li><strong>域名过期查询</strong>：查询域名的注册时间与到期时间</li><li><strong>抖音解析</strong>：解析抖音链接，获取无水印链接</li><li><strong>语言翻译</strong>：自动识别并翻译</li><li><strong>QQ 电脑在线状态</strong>：查询 QQ 的电脑在线状态</li><li><strong>微信域名安全检测</strong>：检测域名在微信是否报毒</li><li><strong>mrw.so 短网址</strong>：提供 mrw.so 短网址的生成与还原服务</li><li><strong>机器人云黑名单</strong>：查询各类自动进群机器人等等</li><li><strong>网站 favicon 图标获取</strong>：获取网站的 favicon.ico 图标</li><li><strong>网易云音乐解析</strong>：在线解析网易云音乐</li><li><strong>QQ 手游+微视一键加速</strong>：QQ 手游+微视一键加速（0.2+0.5）天</li><li><strong>三合一收款码</strong>：合并 QQ，微信和支付宝收款码为一个二维码</li><li><strong>获取 QQ 群加群链接</strong>：只需要 QQ 群号码即可获取加群链接</li></ul><h2 id="持续更新中"><a href="#持续更新中" class="headerlink" title="持续更新中"></a>持续更新中</h2>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E4%BD%9C%E5%93%81%E6%A1%88%E4%BE%8B/">作品案例</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Python/">Python</category>
      
      
      <comments>https://blog.eurkon.com/post/ee499a0b.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Butterfly 微博热搜侧边栏</title>
      <link>https://blog.eurkon.com/post/38b005e1.html</link>
      <guid>https://blog.eurkon.com/post/38b005e1.html</guid>
      <pubDate>Thu, 03 Jun 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文教程主要针对 Hexo Butterfly 主题博客，使用 Vercel API 爬取微博热搜，具体效果可以查看本站侧边栏微博热搜板块。</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文教程主要针对 Hexo Butterfly 主题博客，使用 Vercel API 爬取微博热搜，具体效果可以查看本站侧边栏微博热搜板块。</p><blockquote><ul><li><code>2021-09-07</code> 新浪微博热搜增加“<strong>音</strong>”、“<strong>影</strong>”、“<strong>剧</strong>”、“<strong>综</strong>”等标签，教程同步增加相应的 css 样式。</li><li><code>2021-09-28</code> 由于微博热搜改为了异步加载，旧的爬取方法暂时不能用了，可以去 <a href="https://github.com/Eurkon/weibo-top-api">GitHub</a> 更新代码，也可以参考 Python 代码，可以使用其他方法（可能会出现跨域）请求 <a href="https://weibo.com/ajax/side/hotSearch">微博热搜数据</a>。</li><li><code>2023-03-20</code> 文章 weibo-top-api.vercel.app 地址已失效，vercel 需要绑定域名使用，可以改成 weibo.eurkon.com，尽量自己新建 Vercel。</li></ul></blockquote><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/butterfly/butterfly_weibo_aside.png" alt="Butterfly 微博热搜侧边栏"></p><h2 id="card-weibo-js"><a href="#card-weibo-js" class="headerlink" title="card_weibo.js"></a>card_weibo.js</h2><p>可以使用博主的 <code>card_weibo.js</code> 地址，或者自己创建 <code>card_weibo.js</code> 文件，具体代码如下：</p><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">fetch(<span class="string">&#x27;https://weibo-top-api.vercel.app/api&#x27;</span>).then(<span class="function"><span class="params">data</span> =&gt;</span> data.json()).then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> html = <span class="string">&#x27;&lt;style&gt;.weibo-new&#123;background:#ff3852&#125;.weibo-hot&#123;background:#ff9406&#125;.weibo-jyzy&#123;background:#ffc000&#125;.weibo-recommend&#123;background:#00b7ee&#125;.weibo-adrecommend&#123;background:#febd22&#125;.weibo-friend&#123;background:#8fc21e&#125;.weibo-boom&#123;background:#bd0000&#125;.weibo-topic&#123;background:#ff6f49&#125;.weibo-topic-ad&#123;background:#4dadff&#125;.weibo-boil&#123;background:#f86400&#125;#weibo-container&#123;overflow-y:auto;-ms-overflow-style:none;scrollbar-width:none&#125;#weibo-container::-webkit-scrollbar&#123;display:none&#125;.weibo-list-item&#123;display:flex;flex-direction:row;justify-content:space-between;flex-wrap:nowrap&#125;.weibo-title&#123;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:auto&#125;.weibo-num&#123;float:right&#125;.weibo-hotness&#123;display:inline-block;padding:0 6px;transform:scale(.8) translateX(-3px);color:#fff;border-radius:8px&#125;&lt;/style&gt;&#x27;</span></span><br><span class="line">  html += <span class="string">&#x27;&lt;div class=&quot;weibo-list&quot;&gt;&#x27;</span></span><br><span class="line">  <span class="keyword">let</span> hotness = &#123;</span><br><span class="line">    <span class="string">&#x27;爆&#x27;</span>: <span class="string">&#x27;weibo-boom&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;热&#x27;</span>: <span class="string">&#x27;weibo-hot&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;沸&#x27;</span>: <span class="string">&#x27;weibo-boil&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;新&#x27;</span>: <span class="string">&#x27;weibo-new&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;荐&#x27;</span>: <span class="string">&#x27;weibo-recommend&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;音&#x27;</span>: <span class="string">&#x27;weibo-jyzy&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;影&#x27;</span>: <span class="string">&#x27;weibo-jyzy&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;剧&#x27;</span>: <span class="string">&#x27;weibo-jyzy&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;综&#x27;</span>: <span class="string">&#x27;weibo-jyzy&#x27;</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> item <span class="keyword">of</span> data) &#123;</span><br><span class="line">    html += <span class="string">&#x27;&lt;div class=&quot;weibo-list-item&quot;&gt;&lt;div class=&quot;weibo-hotness &#x27;</span> + hotness[(item.hot || <span class="string">&#x27;荐&#x27;</span>)] + <span class="string">&#x27;&quot;&gt;&#x27;</span> + (item.hot || <span class="string">&#x27;荐&#x27;</span>) + <span class="string">&#x27;&lt;/div&gt;&#x27;</span></span><br><span class="line">      + <span class="string">&#x27;&lt;span class=&quot;weibo-title&quot;&gt;&lt;a title=&quot;&#x27;</span> + item.title + <span class="string">&#x27;&quot;href=&quot;&#x27;</span> + item.url + <span class="string">&#x27;&quot; target=&quot;_blank&quot; rel=&quot;external nofollow noreferrer&quot;&gt;&#x27;</span> + item.title + <span class="string">&#x27;&lt;/a&gt;&lt;/span&gt;&#x27;</span></span><br><span class="line">      + <span class="string">&#x27;&lt;div class=&quot;weibo-num&quot;&gt;&lt;span&gt;&#x27;</span> + item.num + <span class="string">&#x27;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&#x27;</span></span><br><span class="line">  &#125;</span><br><span class="line">  html += <span class="string">&#x27;&lt;/div&gt;&#x27;</span></span><br><span class="line">  <span class="built_in">document</span>.getElementById(<span class="string">&#x27;weibo-container&#x27;</span>).innerHTML = html</span><br><span class="line">&#125;).catch(<span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(error);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><h2 id="config-butterfly"><a href="#config-butterfly" class="headerlink" title="_config.butterfly"></a>_config.butterfly</h2><p>在 <code>[Blogroot]\_config.butterfly.yml</code> 的侧边栏配置项和 CDN 配置项增加以下内容。</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">  # aside (側邊欄)</span><br><span class="line">  # --------------------------------------</span><br><span class="line">  aside:</span><br><span class="line">    enable: true</span><br><span class="line">    ...</span><br><span class="line"><span class="addition">+   card_weibo:</span></span><br><span class="line"><span class="addition">+     enable: true</span></span><br><span class="line"></span><br><span class="line">  # CDN</span><br><span class="line">  # Don&#x27;t modify the following settings unless you know how they work</span><br><span class="line">  # 非必要請不要修改</span><br><span class="line">  CDN:</span><br><span class="line">    # main</span><br><span class="line">    main_css: /css/index.css</span><br><span class="line">    jquery: https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js</span><br><span class="line">    main: /js/main.js</span><br><span class="line">    utils: /js/utils.js</span><br><span class="line"><span class="addition">+   card_weibo: https://npm.elemecdn.com/eurkon-cdn/hexo/js/card_weibo.js ## 或者填写自己的 js 地址</span></span><br></pre></td></tr></table></figure></p><h2 id="card-weibo-pug"><a href="#card-weibo-pug" class="headerlink" title="card_weibo.pug"></a>card_weibo.pug</h2><p>在 <code>[Blogroot]\themes\butterfly\layout\includes\widget</code> 目录下创建 <code>card_weibo.pug</code> 文件，添加以下内容。</p><p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">if theme.aside.card_weibo.enable</span><br><span class="line">  .card-widget.card-weibo</span><br><span class="line">    .card-content</span><br><span class="line">      .item-headline</span><br><span class="line">        i.fab.fa-weibo</span><br><span class="line">        span&#x3D; _p(&#39;微博热搜&#39;)</span><br><span class="line">      #weibo-container(style&#x3D;&quot;width: 100%; height: 150px;font-size: 95%;&quot;)</span><br><span class="line">  script(defer data-pjax src&#x3D;url_for(theme.CDN.card_weibo))</span><br></pre></td></tr></table></figure></p><h2 id="index-pug"><a href="#index-pug" class="headerlink" title="index.pug"></a>index.pug</h2><p>在 <code>[Blogroot]\themes\butterfly\layout\includes\widget\index.pug</code> 文件中增加以下内容。</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">  #aside-content.aside-content</span><br><span class="line">    //- post</span><br><span class="line">    if is_post()</span><br><span class="line">      if showToc &amp;&amp; theme.toc.style_simple</span><br><span class="line">        .sticky_layout</span><br><span class="line">          include ./card_post_toc.pug</span><br><span class="line">      else</span><br><span class="line">        !=partial(&#x27;includes/widget/card_author&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">        !=partial(&#x27;includes/widget/card_announcement&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">        .sticky_layout</span><br><span class="line">          if showToc</span><br><span class="line">            include ./card_post_toc.pug</span><br><span class="line"><span class="addition">+         !=partial(&#x27;includes/widget/card_weibo&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span></span><br><span class="line">          !=partial(&#x27;includes/widget/card_recent_post&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">          !=partial(&#x27;includes/widget/card_ad&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">    else</span><br><span class="line">      //- page</span><br><span class="line">      !=partial(&#x27;includes/widget/card_author&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">      !=partial(&#x27;includes/widget/card_announcement&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">      .sticky_layout</span><br><span class="line"><span class="addition">+       !=partial(&#x27;includes/widget/card_weibo&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span></span><br><span class="line">        !=partial(&#x27;includes/widget/card_recent_post&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">        !=partial(&#x27;includes/widget/card_ad&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">        !=partial(&#x27;includes/widget/card_newest_comment&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">        !=partial(&#x27;includes/widget/card_categories&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">        !=partial(&#x27;includes/widget/card_tags&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">        !=partial(&#x27;includes/widget/card_archives&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">        !=partial(&#x27;includes/widget/card_webinfo&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br><span class="line">        !=partial(&#x27;includes/widget/card_self&#x27;, &#123;&#125;, &#123;cache: true&#125;)</span><br></pre></td></tr></table></figure></p><h2 id="自建-Vercel-API（可选）"><a href="#自建-Vercel-API（可选）" class="headerlink" title="自建 Vercel API（可选）"></a>自建 Vercel API（可选）</h2><p><a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/Eurkon/weibo-top-api"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://github-readme-stats.eurkon.com/api/pin/?username=Eurkon&repo=weibo-top-api&show_owner=true"></a></p><p>虽然 Vercel 的访问应当没有次数限制，但是不排除存在因访问次数过多而限流等限制。所以还是建议各位自建 API。使用 Vercel 部署，完全免费。且无需服务器。</p><p>部署完成后将获取到的默认域名替换 <code>card_weibo.js</code> 中的 <code>weibo-top-api.vercel.app</code>，如：</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">- fetch(&#x27;https://weibo-top-api.vercel.app/api&#x27;).then(data =&gt; data.json()).then(data =&gt; &#123;</span></span><br><span class="line"><span class="addition">+ fetch(&#x27;https://域名/api&#x27;).then(data =&gt; data.json()).then(data =&gt; &#123;</span></span><br></pre></td></tr></table></figure></p><h2 id="Hexo-三连"><a href="#Hexo-三连" class="headerlink" title="Hexo 三连"></a>Hexo 三连</h2><p>执行 Hexo 三连</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo g &amp;&amp; hexo s</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.eurkon.com/tags/Butterfly/">Butterfly</category>
      
      
      <comments>https://blog.eurkon.com/post/38b005e1.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Spark 面试题解析</title>
      <link>https://blog.eurkon.com/post/cccf27af.html</link>
      <guid>https://blog.eurkon.com/post/cccf27af.html</guid>
      <pubDate>Wed, 26 May 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Spark-内核&quot;&gt;&lt;a href=&quot;#Spark-内核&quot; class=&quot;headerlink&quot; title=&quot;Spark 内核&quot;&gt;&lt;/a&gt;Spark 内核&lt;/h2&gt;&lt;h3 id=&quot;Spark-的有几种部署模式，每种模式特点？&quot;&gt;&lt;a href=&quot;#Spark-的</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Spark-内核"><a href="#Spark-内核" class="headerlink" title="Spark 内核"></a>Spark 内核</h2><h3 id="Spark-的有几种部署模式，每种模式特点？"><a href="#Spark-的有几种部署模式，每种模式特点？" class="headerlink" title="Spark 的有几种部署模式，每种模式特点？"></a>Spark 的有几种部署模式，每种模式特点？</h3><ul><li><strong>本地模式</strong>：Spark 不一定非要跑在 Hadoop 集群，可以在本地，起多个线程的方式来指定。将 Spark 应用以多线程的方式直接运行在本地，一般都是为了方便调试，本地模式分三类：<ul><li><code>local</code>：只启动一个 Executor</li><li><code>local[k]</code>:启动 k 个 Executor</li><li><code>local[*]</code>：启动跟 CPU 数目相同的 Executor</li></ul></li><li><strong>standalone 模式</strong>：分布式部署集群，自带完整的服务，资源管理和任务监控是 Spark 自己监控，这个模式也是其他模式的基础。</li><li><strong>Spark on Yarn 模式</strong>：分布式部署集群，资源和任务监控交给 YARN 管理，但是目前仅支持粗粒度资源分配方式，包含 cluster 和 client 运行模式，cluster 适合生产，Driver 运行在集群子节点，具有容错功能，client 适合调试，Driver 运行在客户端。</li><li><strong>Spark On Mesos 模式</strong>：官方推荐这种模式（当然，原因之一是血缘关系）。正是由于 Spark 开发之初就考虑到支持 Mesos，因此，目前而言，Spark 运行在 Mesos 上会比运行在 YARN 上更加灵活，更加自然。用户可选择两种调度模式之一运行自己的应用程序：<ul><li>粗粒度模式（Coarse-grained Mode）：每个应用程序的运行环境由一个 Driver 和若干个 Executor 组成，其中，每个 Executor 占用若干资源，内部可运行多个 Task（对应多少个“slot”）。应用程序的各个任务正式运行之前，需要将运行环境中的资源全部申请好，且运行过程中要一直占用这些资源，即使不用，最后程序运行结束后，回收这些资源。</li><li>细粒度模式（Fine-grained Mode）：鉴于粗粒度模式会造成大量资源浪费，Spark On Mesos 还提供了另外一种调度模式：细粒度模式，这种模式类似于现在的云计算，思想是按需分配。</li></ul></li></ul><h3 id="Spark-为什么比-MapReduce-快？"><a href="#Spark-为什么比-MapReduce-快？" class="headerlink" title="Spark 为什么比 MapReduce 快？"></a>Spark 为什么比 MapReduce 快？</h3><ul><li>基于内存计算，减少低效的磁盘交互；</li><li>高效的调度算法，基于 DAG；</li><li>容错机制 Lineage，精华部分就是 DAG 和 Lineage。</li></ul><h3 id="简单说一下-Hadoop-和-Spark-的-shuffle-相同和差异？"><a href="#简单说一下-Hadoop-和-Spark-的-shuffle-相同和差异？" class="headerlink" title="简单说一下 Hadoop 和 Spark 的 shuffle 相同和差异？"></a>简单说一下 Hadoop 和 Spark 的 shuffle 相同和差异？</h3><ul><li>从 high-level 的角度来看，两者并没有大的差别。都是将 mapper（Spark 里是 ShuffleMapTask）的输出进行 partition，不同的 partition 送到不同的 reducer（Spark 里 reducer 可能是下一个 stage 里的 ShuffleMapTask，也可能是 ResultTask）。Reducer 以内存作缓冲区，边 shuffle 边 aggregate 数据，等到数据 aggregate 好以后进行 reduce（Spark 里可能是后续的一系列操作）。</li><li>从 low-level 的角度来看，两者差别不小。Hadoop MapReduce 是 sort-based，进入 combine 和 reduce 的 records 必须先 sort。这样的好处在于 combine/reduce 可以处理大规模的数据，因为其输入数据可以通过外排得到（mapper 对每段数据先做排序，reducer 的 shuffle 对排好序的每段数据做归并）。目前的 Spark 默认选择的是 hash-based，通常使用 HashMap 来对 shuffle 来的数据进行 aggregate，不会对数据进行提前排序。如果用户需要经过排序的数据，那么需要自己调用类似 sortByKey 的操作；如果你是 Spark 1.1 的用户，可以将 <code>spark.shuffle.manager</code> 设置为 sort，则会对数据进行排序。在 Spark 1.2 中，sort 将作为默认的 Shuffle 实现。</li><li>从实现角度来看，两者也有不少差别。Hadoop MapReduce 将处理流程划分出明显的几个阶段：map，spill，merge，shuffle，sort，reduce 等。每个阶段各司其职，可以按照过程式的编程思想来逐一实现每个阶段的功能。在 Spark 中，没有这样功能明确的阶段，只有不同的 stage 和一系列的 transformation，所以 spill，merge，aggregate 等操作需要蕴含在 transformation 中。</li></ul><p>如果我们将 map 端划分数据、持久化数据的过程称为 shuffle write，而将 reducer 读入数据、aggregate 数据的过程称为 shuffle read。那么在 Spark 中，问题就变为怎么在 job 的逻辑或者物理执行图中加入 shuffle write 和 shuffle read 的处理逻辑？以及两个处理逻辑应该怎么高效实现？</p><p>shuffle write 由于不要求数据有序，shuffle write 的任务很简单：将数据 partition 好，并持久化。之所以要持久化，一方面是要减少内存存储空间压力，另一方面也是为了 fault-tolerance。</p><h3 id="Spark-工作机制？Spark-应用程序的执行过程？"><a href="#Spark-工作机制？Spark-应用程序的执行过程？" class="headerlink" title="Spark 工作机制？Spark 应用程序的执行过程？"></a>Spark 工作机制？Spark 应用程序的执行过程？</h3><ol><li>构建 Application 的运行环境，Driver 创建一个 SparkContext；</li><li>SparkContext 向资源管理器（Standalone、Mesos、Yarn）申请 Executor 资源，资源管理器启动 StandaloneExecutorBackend（Executor）;</li><li>Executor 向 SparkContext 申请 Task;</li><li>SparkContext 将应用程序分发给 Executor;</li><li>SparkContext 就建成 DAG 图，DAG Scheduler 将 DAG 图解析成 Stage，每个 Stage 有多个 Task，形成 TaskSet 发送给 Task Scheduler，由 Task Scheduler 将 Task 发送给 Executor 运行；</li><li>Task 在 Executor 上运行，运行完释放所有资源。</li></ol><h3 id="Spark-的优化怎么做？"><a href="#Spark-的优化怎么做？" class="headerlink" title="Spark 的优化怎么做？"></a>Spark 的优化怎么做？</h3><p>Spark 调优比较复杂，但是大体可以分为三个方面来进行</p><ul><li>平台层面的调优：防止不必要的 jar 包分发，提高数据的本地性，选择高效的存储格式如 parquet；</li><li>应用程序层面的调优：过滤操作符的优化降低过多小任务，降低单条记录的资源开销，处理数据倾斜，复用 RDD 进行缓存，作业并行化执行等等；</li><li>JVM 层面的调优：设置合适的资源量，设置合理的 JVM，启用高效的序列化方法如 Kyro，增大 off-heap 内存等等；</li></ul><h3 id="数据本地性是在哪个环节确定的？"><a href="#数据本地性是在哪个环节确定的？" class="headerlink" title="数据本地性是在哪个环节确定的？"></a>数据本地性是在哪个环节确定的？</h3><p>具体的 Task 运行的那台机器上，DAG 划分 Stage 的时候确定的。</p><h3 id="RDD-的弹性表现在哪几点？"><a href="#RDD-的弹性表现在哪几点？" class="headerlink" title="RDD 的弹性表现在哪几点？"></a>RDD 的弹性表现在哪几点？</h3><ul><li>自动的进行内存和磁盘的存储切换；</li><li>基于 Lineage 的高效容错；</li><li>Task 如果失败会自动进行特定次数的重试；</li><li>Stage 如果失败会自动进行特定次数的重试，而且只会计算失败的分片；</li><li>checkpoint 和 persist，数据计算之后持久化缓存；</li><li>数据调度弹性，DAG TASK 调度和资源无关；</li><li>数据分片的高度弹性。</li></ul><h3 id="RDD-有哪些缺陷？"><a href="#RDD-有哪些缺陷？" class="headerlink" title="RDD 有哪些缺陷？"></a>RDD 有哪些缺陷？</h3><ul><li>不支持细粒度的写和更新操作，Spark 写数据是粗粒度的。所谓粗粒度，就是批量写入数据，为了提高效率。但是读数据是细粒度的也就是说可以一条条的读。</li><li>不支持增量迭代计算，Flink 支持</li></ul><h3 id="Spark-的-shuffle-过程？"><a href="#Spark-的-shuffle-过程？" class="headerlink" title="Spark 的 shuffle 过程？"></a>Spark 的 shuffle 过程？</h3><p>从下面三点去展开</p><ul><li>shuffle 过程的划分</li><li>shuffle 的中间结果如何存储</li><li>shuffle 的数据如何拉取过来</li></ul><h3 id="Spark-的数据本地性有哪几种？"><a href="#Spark-的数据本地性有哪几种？" class="headerlink" title="Spark 的数据本地性有哪几种？"></a>Spark 的数据本地性有哪几种？</h3><p>Spark 中的数据本地性有三种：</p><ul><li>PROCESS_LOCAL 是指读取缓存在本地节点的数据</li><li>NODE_LOCAL 是指读取本地节点硬盘数据</li><li>ANY 是指读取非本地节点数据</li></ul><p>通常读取数据 PROCESS_LOCAL &gt; NODE_LOCAL &gt; ANY，尽量使数据以 PROCESS_LOCAL 或 NODE_LOCAL 方式读取。其中 PROCESS_LOCAL 还和 cache 有关，如果 RDD 经常用的话将该 RDD cache 到内存中，注意，由于 cache 是 lazy 的，所以必须通过一个 action 的触发，才能真正的将该 RDD cache 到内存中。</p><h3 id="Spark-为什么要持久化，一般什么场景下要进行-persist-操作？"><a href="#Spark-为什么要持久化，一般什么场景下要进行-persist-操作？" class="headerlink" title="Spark 为什么要持久化，一般什么场景下要进行 persist 操作？"></a>Spark 为什么要持久化，一般什么场景下要进行 persist 操作？</h3><p>Spark 所有复杂一点的算法都会有 persist 身影，Spark 默认数据放在内存，Spark 很多内容都是放在内存的，非常适合高速迭代，1000 个步骤只有第一个输入数据，中间不产生临时数据，但分布式系统风险很高，所以容易出错，就要容错，RDD 出错或者分片可以根据血统算出来，如果没有对父 RDD 进行 persist 或者 cache 的话，就需要重头做。以下场景会使用 persist：</p><ul><li>某个步骤计算非常耗时，需要进行 persist 持久化；</li><li>计算链条非常长，重新恢复要算很多步骤；</li><li>checkpoint 所在的 RDD 要持久化 persist。checkpoint 前要持久化，写个 rdd.cache 或者 rdd.persist，将结果保存起来，再写 checkpoint 操作，这样执行起来会非常快，不需要重新计算 RDD 链条了。checkpoint 之前一定会进行 persist；</li><li>shuffle 之后要 persist，shuffle 要进行网络传输，风险很大，数据丢失重来，恢复代价很大；</li><li>shuffle 之前进行 persist，框架默认将数据持久化到磁盘，这个是框架自动做的。</li></ul><h3 id="介绍一下-join-操作优化经验？"><a href="#介绍一下-join-操作优化经验？" class="headerlink" title="介绍一下 join 操作优化经验？"></a>介绍一下 join 操作优化经验？</h3><p>join 其实常见的就分为两类：<strong>map-side join</strong> 和 <strong>reduce-side join</strong>。当大表和小表 join 时，用 map-side join 能显著提高效率。将多份数据进行关联是数据处理过程中非常普遍的用法，不过在分布式计算系统中，这个问题往往会变的非常麻烦，因为框架提供的 join 操作一般会将所有数据根据 key 发送到所有的 reduce 分区中去，也就是 shuffle 的过程。造成大量的网络以及磁盘 IO 消耗，运行效率极其低下，这个过程一般被称为 reduce-side-join。如果其中有张表较小的话，我们则可以自己实现在 map 端实现数据关联，跳过大量数据进行 shuffle 的过程，运行时间得到大量缩短，根据不同数据可能会有几倍到数十倍的性能提升。</p><h3 id="描述-Yarn-执行一个任务的过程？"><a href="#描述-Yarn-执行一个任务的过程？" class="headerlink" title="描述 Yarn 执行一个任务的过程？"></a>描述 Yarn 执行一个任务的过程？</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/spark_yarn_workflow.png" alt="Yarn 工作流"></p><ol><li>客户端 client 向 ResourceManager 提交 Application，ResourceManager 接受 Application 并根据集群资源状况选取一个 node 来启动 Application 的任务调度器 Driver（ApplicationMaster）。</li><li>ResourceManager 找到那个 node，命令其该 node 上的 nodeManager 来启动一个新的 JVM 进程运行程序的 Driver（ApplicationMaster）部分，Driver（ApplicationMaster）启动时会首先向 ResourceManager 注册，说明由自己来负责当前程序的运行。</li><li>Driver（ApplicationMaster）开始下载相关 jar 包等各种资源，基于下载的 jar 等信息决定向 ResourceManager 申请具体的资源内容。</li><li>ResourceManager 接受到 Driver（ApplicationMaster）提出的申请后，会最大化的满足资源分配请求，并发送资源的元数据信息给 Driver（ApplicationMaster）。</li><li>Driver（ApplicationMaster）收到发过来的资源元数据信息后会根据元数据信息发指令给具体机器上的 NodeManager，让其启动具体的 Container。</li><li>NodeManager 收到 Driver 发来的指令，启动 Container，Container 启动后必须向 Driver（ApplicationMaster）注册。</li><li>Driver（ApplicationMaster）收到 Container 的注册，开始进行任务的调度和计算，直到任务完成。</li></ol><p>注意：如果 ResourceManager 第一次没有能够满足 Driver（ApplicationMaster）的资源请求 ，后续发现有空闲的资源，会主动向 Driver（ApplicationMaster）发送可用资源的元数据信息以提供更多的资源用于当前程序的运行。</p><h3 id="Spark-on-Yarn-模式有哪些优点？"><a href="#Spark-on-Yarn-模式有哪些优点？" class="headerlink" title="Spark on Yarn 模式有哪些优点？"></a>Spark on Yarn 模式有哪些优点？</h3><ul><li>与其他计算框架共享集群资源（Spark 框架与 MapReduce 框架同时运行，如果不用 Yarn 进行资源分配，MapReduce 分到的内存资源会很少，效率低下）；资源按需分配，进而提高集群资源利用等。</li><li>相较于 Spark 自带的 Standalone 模式，Yarn 的资源分配更加细致。</li><li>Application 部署简化，例如 Spark，Storm 等多种框架的应用由客户端提交后，由 Yarn 负责资源的管理和调度，利用 Container 作为资源隔离的单位，以它为单位去使用内存、CPU 等。</li><li>Yarn 通过队列的方式，管理同时运行在 Yarn 集群中的多个服务，可根据不同类型的应用程序负载情况，调整对应的资源使用量，实现资源弹性管理。</li></ul><h3 id="谈谈你对-Container-的理解？"><a href="#谈谈你对-Container-的理解？" class="headerlink" title="谈谈你对 Container 的理解？"></a>谈谈你对 Container 的理解？</h3><ul><li>Container 作为资源分配和调度的基本单位，其中封装了的资源如内存，CPU，磁盘，网络带宽等。目前 YARN 仅仅封装内存和 CPU；</li><li>Container 由 ApplicationMaster 向 ResourceManager 申请的，由 ResourceManager 中的资源调度器异步分配给 ApplicationMaster；</li><li>Container 的运行是由 ApplicationMaster 向资源所在的 NodeManager 发起的，Container 运行时需提供内部执行的任务命令。</li></ul><h3 id="Spark-使用-parquet-文件存储格式能带来哪些好处？"><a href="#Spark-使用-parquet-文件存储格式能带来哪些好处？" class="headerlink" title="Spark 使用 parquet 文件存储格式能带来哪些好处？"></a>Spark 使用 parquet 文件存储格式能带来哪些好处？</h3><ul><li>如果说 HDFS 是大数据时代分布式文件系统首选标准，那么 parquet 则是整个大数据时代文件存储格式实时首选标准。</li><li>速度更快：从使用 Spark SQL 操作普通文件 CSV 和 parquet 文件速度对比上看，绝大多数情况会比使用 csv 等普通文件速度提升 10 倍左右，在一些普通文件系统无法在 Spark 上成功运行的情况下，使用 parquet 很多时候可以成功运行。</li><li>parquet 的压缩技术非常稳定出色，在 Spark SQL 中对压缩技术的处理可能无法正常的完成工作（例如会导致 lost task，lost executor）但是此时如果使用 parquet 就可以正常的完成。</li><li>极大的减少磁盘 IO，通常情况下能够减少 75% 的存储空间，由此可以极大的减少 Spark SQL 处理数据的时候的数据输入内容，尤其是在 Spark1.6x 中有个下推过滤器在一些情况下可以极大的减少磁盘的 IO 和内存的占用。</li><li>Spark 1.6x parquet 方式极大的提升了扫描的吞吐量，极大提高了数据的查找速度 Spark1.6x 和 Spark1.5x 相比而言，提升了大约 1 倍的速度，在 Spark1.6x 中，操作 parquet 时候 CPU 也进行了极大的优化，有效的降低了 CPU 消耗。</li><li>采用 parquet 可以极大的优化 Spark 的调度和执行。我们测试 Spark 如果用 parquet 可以有效的减少 Stage 的执行消耗，同时可以优化执行路径。</li></ul><h3 id="介绍-partition-和-block-有什么关联关系？"><a href="#介绍-partition-和-block-有什么关联关系？" class="headerlink" title="介绍 partition 和 block 有什么关联关系？"></a>介绍 partition 和 block 有什么关联关系？</h3><ul><li>HDFS 中的 block 是分布式存储的最小单元，等分，可设置冗余，这样设计有一部分磁盘空间的浪费，但是整齐的 block 大小，便于快速找到、读取对应的内容；</li><li>Spark 中的 partition 是弹性分布式数据集 RDD 的最小单元，RDD 是由分布在各个节点上的 partition 组成的。partition 是指的 Spark 在计算过程中，生成的数据在计算空间内最小单元，同一份数据（RDD）的 partition 大小不一，数量不定，是根据 application 里的算子和最初读入的数据分块数量决定；</li><li>block 位于存储空间、partition 位于计算空间，block 的大小是固定的、partition 大小是不固定的，是从 2 个不同的角度去看数据。</li></ul><h3 id="不需要排序的-hash-shuffle-是否一定比需要排序的-sort-shuffle-速度快？"><a href="#不需要排序的-hash-shuffle-是否一定比需要排序的-sort-shuffle-速度快？" class="headerlink" title="不需要排序的 hash shuffle 是否一定比需要排序的 sort shuffle 速度快？"></a>不需要排序的 hash shuffle 是否一定比需要排序的 sort shuffle 速度快？</h3><p>不一定，当数据规模小，hash shuffle 快于 sorted shuffle，数据规模大的时候；当数据量大，sorted shuffle 会比 hash shuffle 快很多，因为数量大的有很多小文件，不均匀，甚至出现数据倾斜，消耗内存大，1.x 之前 Spark 使用 hash，适合处理中小规模，1.x 之后，增加了 sorted shuffle，Spark 更能胜任大规模处理了。</p><h3 id="Sort-based-shuffle-的缺陷？"><a href="#Sort-based-shuffle-的缺陷？" class="headerlink" title="Sort-based shuffle 的缺陷？"></a>Sort-based shuffle 的缺陷？</h3><ul><li>如果 mapper 中 task 的数量过大，依旧会产生很多小文件，此时在 shuffle 传递数据到 reducer 的过程中，reduce 会需要同时大量的记录进行反序列化，导致大量的内存消耗和 GC 的巨大负担，造成系统缓慢甚至崩溃。</li><li>如果需要在分片内也进行排序，此时需要进行 mapper 和 reducer 的两次排序。</li></ul><h3 id="spark-storage-memoryFraction-参数的含义，实际生产中如何调优？"><a href="#spark-storage-memoryFraction-参数的含义，实际生产中如何调优？" class="headerlink" title="spark.storage.memoryFraction 参数的含义，实际生产中如何调优？"></a>spark.storage.memoryFraction 参数的含义，实际生产中如何调优？</h3><ul><li><p>用于设置 RDD 持久化数据在 Executor 内存中能占的比例，默认是 0.6，默认 Executor 60% 的内存，可以用来保存持久化的 RDD 数据。根据你选择的不同的持久化策略，如果内存不够时，可能数据就不会持久化，或者数据会写入磁盘；</p></li><li><p>如果持久化操作比较多，可以提高 <code>spark.storage.memoryFraction</code> 参数，使得更多的持久化数据保存在内存中，提高数据的读取性能，如果 shuffle 的操作比较多，有很多的数据读写操作到 JVM 中，那么应该调小一点，节约出更多的内存给 JVM，避免过多的 JVM GC 发生。在 Web UI 中观察如果发现 GC 时间很长，可以设置 <code>spark.storage.memoryFraction</code> 更小一点。</p></li></ul><h3 id="介绍一下你对-Unified-Memory-Management-内存管理模型的理解？"><a href="#介绍一下你对-Unified-Memory-Management-内存管理模型的理解？" class="headerlink" title="介绍一下你对 Unified Memory Management 内存管理模型的理解？"></a>介绍一下你对 Unified Memory Management 内存管理模型的理解？</h3><p>Spark 中的内存使用分为两部分：执行（execution）与存储（storage）。执行内存主要用于 shuffles、joins、sorts 和 aggregations，存储内存则用于缓存或者跨节点的内部数据传输。1.6 之前，对于一个 Executor，内存都由以下部分构成：</p><ul><li>ExecutionMemory：这片内存区域是为了解决 shuffles，joins，sorts 和 aggregations 过程中为了避免频繁 IO 需要的 buffer。通过 <code>spark.shuffle.memoryFraction</code>（默认 0.2）配置。</li><li>StorageMemory：这片内存区域是为了解决 block cache（就是你显示调用 rdd.cache，rdd.persist 等方法），还有就是 broadcasts 以及 task results 的存储。可以通过参数 <code>spark.storage.memoryFraction</code>（默认 0.6）设置。</li><li>OtherMemory：给系统预留的，因为程序本身运行也是需要内存的（默认为 0.2）。</li></ul><p>传统内存管理的不足：</p><ul><li>Shuffle 占用内存 0.2*0.8，内存分配这么少，可能会将数据 spill 到磁盘，频繁的磁盘 IO 是很大的负担，Storage 内存占用 0.6，主要是为了迭代处理。传统的 Spark 内存分配对操作人的要求非常高。（Shuffle 分配内存：ShuffleMemoryManager，TaskMemoryManager，ExecutorMemoryManager）一个 Task 获得全部的 Execution 的 Memory，其他 Task 过来就没有内存了，只能等待；</li><li>默认情况下，Task 在线程中可能会占满整个内存，分片数据特别大的情况下就会出现这种情况，其他 Task 没有内存了，剩下的 cores 就空闲了，这是巨大的浪费。这也是人为操作的不当造成的；</li><li>MEMORY_AND_DISK_SER 的 storage 方式，获得 RDD 的数据是一条条获取，iterator 的方式。如果内存不够（spark.storage.unrollFraction），unroll 的读取数据过程，就是看内存是否足够，如果足够，就下一条。unroll 的 space 是从 Storage 的内存空间中获得的。unroll 的方式失败，就会直接放磁盘；</li><li>默认情况下，Task 在 spill 到磁盘之前，会将部分数据存放到内存上，如果获取不到内存，就不会执行。永无止境的等待，消耗 CPU 和内存；</li></ul><p>​在此基础上，Spark 提出了 UnifiedMemoryManager，不再分 ExecutionMemory 和 Storage Memory，实际上还是分的，只不过是 Execution Memory 访问 Storage Memory，Storage Memory 也可以访问 Execution Memory，如果内存不够，就会去借。</p><h3 id="Spark-有哪两种算子？"><a href="#Spark-有哪两种算子？" class="headerlink" title="Spark 有哪两种算子？"></a>Spark 有哪两种算子？</h3><p>​Transformation（转化）算子和 Action（执行）算子。</p><h3 id="Spark-有哪些聚合类的算子，我们应该尽量避免什么类型的算子？"><a href="#Spark-有哪些聚合类的算子，我们应该尽量避免什么类型的算子？" class="headerlink" title="Spark 有哪些聚合类的算子，我们应该尽量避免什么类型的算子？"></a>Spark 有哪些聚合类的算子，我们应该尽量避免什么类型的算子？</h3><p>​在我们的开发过程中，能避免则尽可能避免使用 reduceByKey、join、distinct、repartition 等会进行 shuffle 的算子，尽量使用 map 类的非 shuffle 算子。这样的话，没有 shuffle 操作或者仅有较少 shuffle 操作的 Spark 作业，可以大大减少性能开销。</p><h3 id="如何从-Kafka-中获取数据？"><a href="#如何从-Kafka-中获取数据？" class="headerlink" title="如何从 Kafka 中获取数据？"></a>如何从 Kafka 中获取数据？</h3><ul><li><p>基于 Receiver 的方式</p><p>这种方式使用 Receiver 来获取数据。Receiver 是使用 Kafka 的高层次 Consumer API 来实现的。receiver 从 Kafka 中获取的数据都是存储在 Spark Executor 的内存中的，然后 Spark Streaming 启动的 job 会去处理那些数据。</p></li><li><p>基于 Direct 的方式</p><p>​这种新的不基于 Receiver 的直接方式，是在 Spark 1.3 中引入的，从而能够确保更加健壮的机制。替代掉使用 Receiver 来接收数据后，这种方式会周期性地查询 Kafka，来获得每个 topic + partition 的最新的 offset，从而定义每个 batch 的 offset 的范围。当处理数据的 job 启动时，就会使用 Kafka 的简单 Consumer API 来获取 Kafka 指定 offset 范围的数据。</p></li></ul><h3 id="RDD-创建有哪几种方式？"><a href="#RDD-创建有哪几种方式？" class="headerlink" title="RDD 创建有哪几种方式？"></a>RDD 创建有哪几种方式？</h3><ul><li>使用程序中的集合创建 RDD</li><li>使用本地文件系统创建 RDD</li><li>使用 HDFS 创建 RDD</li><li>基于数据库 db 创建 RDD</li><li>基于 NoSQL 创建 RDD，如 HBase</li><li>基于 s3 创建 RDD</li><li>基于数据流，如 socket 创建 RDD</li></ul><h3 id="Spark-并行度怎么设置比较合适？"><a href="#Spark-并行度怎么设置比较合适？" class="headerlink" title="Spark 并行度怎么设置比较合适？"></a>Spark 并行度怎么设置比较合适？</h3><p>​Spark 并行度，每个 core 承载 2~4 个 partition，如 32 个 core，那么 64~128 之间的并行度，也就是设置 64~128 个 partition，并行读和数据规模无关，只和内存使用量和 CPU 使用时间有关。</p><h3 id="Spark-如何处理不能被序列化的对象？"><a href="#Spark-如何处理不能被序列化的对象？" class="headerlink" title="Spark 如何处理不能被序列化的对象？"></a>Spark 如何处理不能被序列化的对象？</h3><p>​将不能序列化的内容封装成 object。</p><h3 id="collect-功能是什么，其底层是怎么实现的？"><a href="#collect-功能是什么，其底层是怎么实现的？" class="headerlink" title="collect 功能是什么，其底层是怎么实现的？"></a>collect 功能是什么，其底层是怎么实现的？</h3><p>​Driver 通过 collect 把集群中各个节点的内容收集过来汇总成结果，collect 返回结果是 Array 类型的，collect 把各个节点上的数据抓过来，抓过来数据是 Array 型，collect 对 Array 抓过来的结果进行合并，合并后 Array 中只有一个元素，是 tuple 类型（K-V 类型）的。</p><h3 id="为什么-Spark-Application-在没有获得足够的资源，job-就开始执行了，可能会导致什么什么问题发生？"><a href="#为什么-Spark-Application-在没有获得足够的资源，job-就开始执行了，可能会导致什么什么问题发生？" class="headerlink" title="为什么 Spark Application 在没有获得足够的资源，job 就开始执行了，可能会导致什么什么问题发生？"></a>为什么 Spark Application 在没有获得足够的资源，job 就开始执行了，可能会导致什么什么问题发生？</h3><p>​会导致执行该 job 的时候集群资源不足，导致执行 job 结束也没有分配足够的资源，分配了部分 Executor，该 job 就开始执行 task，应该是 task 的调度线程和 Executor 资源申请是异步的；如果想等待申请完所有的资源再执行 job 的，​需要将 ​<code>spark.scheduler.maxRegisteredResourcesWaitingTime</code> 设置的很大；​<code>spark.scheduler.minRegisteredResourcesRatio</code> 设置为 1，但是应该结合实际考虑，​否则很容易出现长时间分配不到资源，job 一直不能运行的情况。</p><h3 id="map-与-flatMap-的区别？"><a href="#map-与-flatMap-的区别？" class="headerlink" title="map 与 flatMap 的区别？"></a>map 与 flatMap 的区别？</h3><ul><li>map：对 RDD 每个元素转换，文件中的每一行数据返回一个数组对象。</li><li>flatMap：对 RDD 每个元素转换，然后再扁平化。</li></ul><p>​将所有的对象合并为一个对象，文件中的所有行数据仅返回一个数组对象，会抛弃值为 null 的值。</p><h3 id="Spark-on-Mesos-中，什么是的粗粒度分配，什么是细粒度分配，各自的优点和缺点是什么？"><a href="#Spark-on-Mesos-中，什么是的粗粒度分配，什么是细粒度分配，各自的优点和缺点是什么？" class="headerlink" title="Spark on Mesos 中，什么是的粗粒度分配，什么是细粒度分配，各自的优点和缺点是什么？"></a>Spark on Mesos 中，什么是的粗粒度分配，什么是细粒度分配，各自的优点和缺点是什么？</h3><ul><li>粗粒度：启动时就分配好资源，程序启动，后续具体使用就使用分配好的资源，不需要再分配资源；好处：作业特别多时，资源复用率高，适合粗粒度；不好：容易资源浪费，假如一个 job 有 1000 个 Task，完成了 999 个，还有一个没完成，那么使用粗粒度，999 个资源就会闲置在那里，资源浪费。</li><li>细粒度分配：用资源的时候分配，用完了就立即回收资源，启动会麻烦一点，启动一次分配一次，会比较麻烦。</li></ul><h3 id="Driver-的功能是什么？"><a href="#Driver-的功能是什么？" class="headerlink" title="Driver 的功能是什么？"></a>Driver 的功能是什么？</h3><ul><li>一个 Spark 作业运行时包括一个 Driver 进程，也是作业的主进程，具有 main 函数，并且有 SparkContext 的实例，是程序的入口点；</li><li>功能：负责向集群申请资源，向 master 注册信息，负责了作业的调度，负责作业的解析、生成 Stage 并调度 Task 到 Executor 上。包括 DAG Scheduler，Task Scheduler。</li></ul><h3 id="Spark-技术栈有哪些组件，每个组件都有什么功能，适合什么应用场景？"><a href="#Spark-技术栈有哪些组件，每个组件都有什么功能，适合什么应用场景？" class="headerlink" title="Spark 技术栈有哪些组件，每个组件都有什么功能，适合什么应用场景？"></a>Spark 技术栈有哪些组件，每个组件都有什么功能，适合什么应用场景？</h3><p>​可以画一个技术栈图先，然后分别解释下每个组件的功能和场景</p><ul><li>Spark Core：是其它组件的基础，Spark 的内核，主要包含：DAG、RDD、Lineage、Cache、broadcast 等，并封装了底层通讯框架，是 Spark 的基础。</li><li>Spark Streaming：是一个对实时数据流进行高通量、容错处理的流式处理系统，可以对多种数据源（如 Kafka、Flume、Twitter、Zero 和 TCP Socket）进行类似 Map、Reduce 和 Join 等复杂操作，将流式计算分解成一系列短小的批处理作业。</li><li>Spark SQL：Shark 是 SparkSQL 的前身，Spark SQL 的一个重要特点是其能够统一处理关系表和 RDD，使得开发人员可以轻松地使用 SQL 命令进行外部查询，同时进行更复杂的数据分析。</li><li>BlinkDB：是一个用于在海量数据上运行交互式 SQL 查询的大规模并行查询引擎，它允许用户通过权衡数据精度来提升查询响应时间，其数据的精度被控制在允许的误差范围内。</li><li>MLBase：是 Spark 生态圈的一部分专注于机器学习，让机器学习的门槛更低，让一些可能并不了解机器学习的用户也能方便地使用 MLBase。MLBase 分为四部分：MLlib、MLI、ML Optimizer 和 MLRuntime。</li><li>GraphX：是 Spark 中用于图和图并行计算。</li></ul><h3 id="Spark-中-Worker-的主要工作是什么？"><a href="#Spark-中-Worker-的主要工作是什么？" class="headerlink" title="Spark 中 Worker 的主要工作是什么？"></a>Spark 中 Worker 的主要工作是什么？</h3><p>​主要功能：管理当前节点内存，CPU 的使用状况，接收 master 分配过来的资源指令，通过 ExecutorRunner 启动程序分配任务，Worker 就类似于包工头，管理分配新进程，做计算的服务，相当于 process 服务。</p><p>​需要注意的是：</p><ul><li>Worker 不会汇报当前信息给 master，worker 心跳给 master 主要只有 workid，它不会发送资源信息以心跳的方式给 mater，master 分配的时候就知道 Worker，只有出现故障的时候才会发送资源。</li><li>Worker 不会运行代码，具体运行的是 Executor 是可以运行具体 Application 写的业务逻辑代码，操作代码的节点，它不会运行程序的代码的。</li></ul><h3 id="MapReduce-和-Spark-的都是并行计算，那么他们有什么相同和区别？"><a href="#MapReduce-和-Spark-的都是并行计算，那么他们有什么相同和区别？" class="headerlink" title="MapReduce 和 Spark 的都是并行计算，那么他们有什么相同和区别？"></a>MapReduce 和 Spark 的都是并行计算，那么他们有什么相同和区别？</h3><p>​两者都是用 MR 模型来进行并行计算:：</p><ul><li>Hadoop 的一个作业称为 job，job 里面分为 map task 和 reduce task，每个 task 都是在自己的进程中运行的，当 task 结束时，进程也会结束。</li><li>Spark 用户提交的任务成为 Application，一个 Application 对应一个 SparkContext，Application 中存在多个 job，每触发一次 action 操作就会产生一个 job。这些 job 可以并行或串行执行，每个 job 中有多个 Stage，Stage 是 shuffle 过程中 DAG Scheduler 通过 RDD 之间的依赖关系划分 job 而来的，每个 Stage 里面有多个 Task，组成 TaskSet 由 Task Scheduler 分发到各个 Executor 中执行，Executor 的生命周期是和 Application 一样的，即使没有 job 运行也是存在的，所以 Task 可以快速启动读取内存进行计算。</li><li>Hadoop 的 job 只有 map 和 reduce 操作，表达能力比较欠缺而且在 MR 过程中会重复的读写 HDFS，造成大量的 IO 操作，多个 job 需要自己管理关系。</li><li>Spark 的迭代计算都是在内存中进行的，API 中提供了大量的 RDD 操作如 join，groupBy 等，而且通过 DAG 图可以实现良好的容错。</li></ul><h3 id="RDD-机制？"><a href="#RDD-机制？" class="headerlink" title="RDD 机制？"></a>RDD 机制？</h3><p>​RDD 分布式弹性数据集，简单的理解成一种数据结构，是 Spark 框架上的通用货币。所有算子都是基于 RDD 来执行的，不同的场景会有不同的 RDD 实现类，但是都可以进行互相转换。RDD 执行过程中会形成 DAG 图，然后形成 Lineage 保证容错性等。从物理的角度来看 RDD 存储的是 block 和 node 之间的映射。</p><h3 id="什么是-RDD-宽依赖和窄依赖？"><a href="#什么是-RDD-宽依赖和窄依赖？" class="headerlink" title="什么是 RDD 宽依赖和窄依赖？"></a>什么是 RDD 宽依赖和窄依赖？</h3><p>​RDD 和它依赖的 parent RDD(s) 的关系有两种不同的类型，即窄依赖（narrow dependency）和宽依赖（wide dependency）。</p><ul><li>窄依赖指的是每一个 parent RDD 的 Partition 最多被子 RDD 的一个 Partition 使用</li><li>宽依赖指的是多个子 RDD 的 Partition 会依赖同一个 parent RDD 的 Partition。</li></ul><h3 id="cache-和-persist-的区别？"><a href="#cache-和-persist-的区别？" class="headerlink" title="cache 和 persist 的区别？"></a>cache 和 persist 的区别？</h3><p>​cache 和 persist 都是用于将一个 RDD 进行缓存的，这样在之后使用的过程中就不需要重新计算了，可以大大节省程序运行时间</p><ul><li>cache 只有一个默认的缓存级别 <code>MEMORY_ONLY</code>，cache 调用了 persist，而 persist 可以根据情况设置其它的缓存级别；</li><li>Executor 执行的时候，默认 60% 做 cache，40% 做 task 操作，persist 是最根本的函数，最底层的函数。</li></ul><h3 id="cache-后面能不能接其他算子，它是不是-action-操作？"><a href="#cache-后面能不能接其他算子，它是不是-action-操作？" class="headerlink" title="cache 后面能不能接其他算子，它是不是 action 操作？"></a>cache 后面能不能接其他算子，它是不是 action 操作？</h3><ul><li>cache 可以接其他算子，但是接了算子之后，起不到缓存应有的效果，因为会重新触发 cache。</li><li>cache 不是 action 操作</li></ul><h3 id="reduceByKey-是不是-action？"><a href="#reduceByKey-是不是-action？" class="headerlink" title="reduceByKey 是不是 action？"></a>reduceByKey 是不是 action？</h3><p>​不是，很多人都会以为是 action，reduce 是 action。</p><h3 id="RDD-通过-Lineage（记录数据更新）的方式为何很高效？"><a href="#RDD-通过-Lineage（记录数据更新）的方式为何很高效？" class="headerlink" title="RDD 通过 Lineage（记录数据更新）的方式为何很高效？"></a>RDD 通过 Lineage（记录数据更新）的方式为何很高效？</h3><ul><li>lazy 记录了数据的来源，RDD 是不可变的，且是 lazy 级别的，且 RDD 之间构成了链条，lazy 是弹性的基石。由于 RDD 不可变，所以每次操作就产生新的 RDD，不存在全局修改的问题，控制难度下降，所有有计算链条将复杂计算链条存储下来，计算的时候从后往前回溯是上一个 Stage 的结束，要么就 checkpoint。</li><li>记录原数据，是每次修改都记录，代价很大如果修改一个集合，代价就很小，官方说 RDD 是粗粒度的操作，是为了效率，为了简化，每次都是操作数据集合，写或者修改操作，都是基于集合的 RDD 的写操作是粗粒度的，RDD 的读操作既可以是粗粒度的也可以是细粒度，读可以读其中的一条条的记录。</li><li>简化复杂度，是高效率的一方面，写的粗粒度限制了使用场景如网络爬虫，现实世界中，大多数写是粗粒度的场景。</li></ul><h3 id="为什么要进行序列化序列化？"><a href="#为什么要进行序列化序列化？" class="headerlink" title="为什么要进行序列化序列化？"></a>为什么要进行序列化序列化？</h3><p>​可以减少数据的体积，减少存储空间，高效存储和传输数据，不好的是在使用的时候要反序列化，非常消耗 CPU。</p><h3 id="Yarn-中的-Container-是由谁负责销毁的，在-Hadoop-MapReduce-中-Container-可以复用么？"><a href="#Yarn-中的-Container-是由谁负责销毁的，在-Hadoop-MapReduce-中-Container-可以复用么？" class="headerlink" title="Yarn 中的 Container 是由谁负责销毁的，在 Hadoop MapReduce 中 Container 可以复用么？"></a>Yarn 中的 Container 是由谁负责销毁的，在 Hadoop MapReduce 中 Container 可以复用么？</h3><p>​ApplicationMaster 负责销毁，在 Hadoop MapReduce 不可以复用，在 Spark on Yarn 程序 Container 可以复用。</p><h3 id="提交任务时，如何指定-Spark-Application-的运行模式？"><a href="#提交任务时，如何指定-Spark-Application-的运行模式？" class="headerlink" title="提交任务时，如何指定 Spark Application 的运行模式？"></a>提交任务时，如何指定 Spark Application 的运行模式？</h3><ul><li>cluster 模式：<code>./spark-submit --class xx.xx.xx --master yarn --deploy-mode cluster xx.jar</code></li><li>client 模式：<code>./spark-submit --class xx.xx.xx --master yarn --deploy-mode client xx.jar</code></li></ul><h3 id="不启动-Spark-集群-Master-和-Worker-服务，可不可以运行-Spark-程序？"><a href="#不启动-Spark-集群-Master-和-Worker-服务，可不可以运行-Spark-程序？" class="headerlink" title="不启动 Spark 集群 Master 和 Worker 服务，可不可以运行 Spark 程序？"></a>不启动 Spark 集群 Master 和 Worker 服务，可不可以运行 Spark 程序？</h3><p>​可以，只要资源管理器第三方管理就可以，如由 Yarn 管理，Spark 集群不启动也可以使用 Spark；Spark 集群启动的是 Worker 和 Master，这个其实就是资源管理框架，Yarn 中的 ResourceManager 相当于 Master，NodeManager 相当于 Worker，做计算是 Executor，和 Spark 集群的 Worker 和 Manager 可以没关系，归根接底还是 JVM 的运行，只要所在的 JVM 上安装了 Spark 就可以。</p><h3 id="Spark-on-Yarn-Cluster-模式下，ApplicationMaster-和-Driver-是在同一个进程么？"><a href="#Spark-on-Yarn-Cluster-模式下，ApplicationMaster-和-Driver-是在同一个进程么？" class="headerlink" title="Spark on Yarn Cluster 模式下，ApplicationMaster 和 Driver 是在同一个进程么？"></a>Spark on Yarn Cluster 模式下，ApplicationMaster 和 Driver 是在同一个进程么？</h3><p>​是，Driver 位于 ApplicationMaster 进程中。该进程负责申请资源，还负责监控程序、资源的动态情况。</p><h3 id="运行在-Yarn-中-Application-有几种类型的-Container？"><a href="#运行在-Yarn-中-Application-有几种类型的-Container？" class="headerlink" title="运行在 Yarn 中 Application 有几种类型的 Container？"></a>运行在 Yarn 中 Application 有几种类型的 Container？</h3><ul><li>运行 ApplicationMaster 的 Container：这是由 ResourceManager（向内部的资源调度器）申请和启动的，用户提交应用程序时，可指定唯一的 ApplicationMaster 所需的资源；</li><li>运行各类任务的 Container：这是由 ApplicationMaster 向 ResourceManager 申请的，并由 ApplicationMaster 与 NodeManager 通信以启动之。</li></ul><h3 id="Executor-启动时，资源通过哪几个参数指定？"><a href="#Executor-启动时，资源通过哪几个参数指定？" class="headerlink" title="Executor 启动时，资源通过哪几个参数指定？"></a>Executor 启动时，资源通过哪几个参数指定？</h3><ul><li><code>num-executors</code>：Executor 的数量；</li><li><code>executor-memory</code>：每个 Executor 使用的内存；</li><li><code>executor-cores</code>：每个 Executor 分配的 CPU。</li></ul><h3 id="列出你所知道的调度器，说明其工作原理"><a href="#列出你所知道的调度器，说明其工作原理" class="headerlink" title="列出你所知道的调度器，说明其工作原理"></a>列出你所知道的调度器，说明其工作原理</h3><ul><li>FIFO Schedular：默认的调度器，先进先出；</li><li>Capacity Schedular：计算能力调度器，选择占用内存小，优先级高的；</li><li>Fair Schedular：公平调度器，所有 job 占用相同资源。</li></ul><h3 id="导致-Executor-产生-FULL-GC-的原因，可能导致什么问题？"><a href="#导致-Executor-产生-FULL-GC-的原因，可能导致什么问题？" class="headerlink" title="导致 Executor 产生 FULL GC 的原因，可能导致什么问题？"></a>导致 Executor 产生 FULL GC 的原因，可能导致什么问题？</h3><p>​可能导致 Executor 僵死问题，海量数据的 shuffle 和数据倾斜等都可能导致 FULL GC。以 shuffle 为例，伴随着大量的 shuffle 写操作，JVM 的新生代不断 GC，Eden Space 写满了就往 Survivor Space 写，同时超过一定大小的数据会直接写到老生代，当新生代写满了之后，也会把老的数据搞到老生代，如果老生代空间不足了，就触发 FULL GC，还是空间不够，那就 OOM 错误了，此时线程被 Blocked，导致整个 Executor 处理数据的进程被卡住。</p><h3 id="Spark-累加器有哪些特点？"><a href="#Spark-累加器有哪些特点？" class="headerlink" title="Spark 累加器有哪些特点？"></a>Spark 累加器有哪些特点？</h3><ul><li>累加器在全局唯一的，只增不减，记录全局集群的唯一状态；</li><li>在 Executor 中修改它，在 Driver 读取；</li><li>Executor 级别共享的，广播变量是 task 级别的共享，两个 Application 不可以共享累加器，但是同一个 Application 不同的 job 可以共享。</li></ul><h3 id="HashPartitioner-的弊端是什么？"><a href="#HashPartitioner-的弊端是什么？" class="headerlink" title="HashPartitioner 的弊端是什么？"></a>HashPartitioner 的弊端是什么？</h3><p>​HashPartitioner 分区的原理很简单，对于给定的 key，计算其 HashCode，并除于分区的个数取余，如果余数小于 0，则用余数 + 分区的个数，最后返回的值就是这个 key 所属的分区 ID；弊端是数据不均匀，容易导致数据倾斜，极端情况下某几个分区会拥有 RDD 的所有数据。</p><h3 id="RangePartitioner-分区的原理？"><a href="#RangePartitioner-分区的原理？" class="headerlink" title="RangePartitioner 分区的原理？"></a>RangePartitioner 分区的原理？</h3><p>​RangePartitioner 分区则尽量保证每个分区中数据量的均匀，而且分区与分区之间是有序的，也就是说一个分区中的元素肯定都是比另一个分区内的元素小或者大；但是分区内的元素是不能保证顺序的。简单的说就是将一定范围内的数映射到某一个分区内。其原理是水塘抽样。RangePartitioner 作用：将一定范围内的数映射到某一个分区内，在实现中，分界的算法尤为重要。算法对应的函数是 rangeBounds。</p><h3 id="如何理解-Standalone-模式下，Spark-资源分配是粗粒度的？"><a href="#如何理解-Standalone-模式下，Spark-资源分配是粗粒度的？" class="headerlink" title="如何理解 Standalone 模式下，Spark 资源分配是粗粒度的？"></a>如何理解 Standalone 模式下，Spark 资源分配是粗粒度的？</h3><p>Spark 默认情况下资源分配是粗粒度的，也就是说程序在提交时就分配好资源，后面执行的时候使用分配好的资源，除非资源出现了故障才会重新分配。比如 Spark shell 启动，已提交，一注册，哪怕没有任务，Worker 都会分配资源给 Executor。</p><h3 id="union-操作是产生宽依赖还是窄依赖？"><a href="#union-操作是产生宽依赖还是窄依赖？" class="headerlink" title="union 操作是产生宽依赖还是窄依赖？"></a>union 操作是产生宽依赖还是窄依赖？</h3><p>​产生窄依赖。</p><h3 id="窄依赖父-RDD-的-partition-和子-RDD-的-partition-是不是都是一对一的关系？"><a href="#窄依赖父-RDD-的-partition-和子-RDD-的-partition-是不是都是一对一的关系？" class="headerlink" title="窄依赖父 RDD 的 partition 和子 RDD 的 partition 是不是都是一对一的关系？"></a>窄依赖父 RDD 的 partition 和子 RDD 的 partition 是不是都是一对一的关系？</h3><p>​不一定，除了一对一的窄依赖，还包含一对固定个数的窄依赖（就是对父 RDD 的依赖的 Partition 的数量不会随着 RDD 数量规模的改变而改变），比如 join 操作的每个 partition 仅仅和已知的 partition 进行 join，这个 join 操作是窄依赖，依赖固定数量的父 RDD，因为是确定的 partition 关系。</p><h3 id="Hadoop-中，MapReduce-操作的-mapper-和-reducer-阶段相当于-Spark-中的哪几个算子？"><a href="#Hadoop-中，MapReduce-操作的-mapper-和-reducer-阶段相当于-Spark-中的哪几个算子？" class="headerlink" title="Hadoop 中，MapReduce 操作的 mapper 和 reducer 阶段相当于 Spark 中的哪几个算子？"></a>Hadoop 中，MapReduce 操作的 mapper 和 reducer 阶段相当于 Spark 中的哪几个算子？</h3><p>​相当于 Spark 中的 map 算子和 reduceByKey 算子，当然还是有点区别的，MR 会自动进行排序的，Spark 要看你用的是什么 Partitioner。</p><h3 id="什么是-shuffle，以及为什么需要-shuffle？"><a href="#什么是-shuffle，以及为什么需要-shuffle？" class="headerlink" title="什么是 shuffle，以及为什么需要 shuffle？"></a>什么是 shuffle，以及为什么需要 shuffle？</h3><p>​shuffle 中文翻译为洗牌，需要 shuffle 的原因是：某种具有共同特征的数据汇聚到一个计算节点上进行计算。</p><h3 id="Spark-中的-HashShuffle-的有哪些不足？"><a href="#Spark-中的-HashShuffle-的有哪些不足？" class="headerlink" title="Spark 中的 HashShuffle 的有哪些不足？"></a>Spark 中的 HashShuffle 的有哪些不足？</h3><ul><li>shuffle 产生海量的小文件在磁盘上，此时会产生大量耗时的、低效的 IO 操作；</li><li>容易导致内存不够用，由于内存需要保存海量的文件操作句柄和临时缓存信息，如果数据处理规模比较大的话，容易出现 OOM；</li><li>容易出现数据倾斜，导致 OOM。</li></ul><h3 id="consolidate-是如何优化-Hash-shuffle-时在-map-端产生的小文件？"><a href="#consolidate-是如何优化-Hash-shuffle-时在-map-端产生的小文件？" class="headerlink" title="consolidate 是如何优化 Hash shuffle 时在 map 端产生的小文件？"></a>consolidate 是如何优化 Hash shuffle 时在 map 端产生的小文件？</h3><ul><li>consolidate 为了解决 Hash Shuffle 同时打开过多文件导致 Writer handler 内存使用过大以及产生过多文件导致大量的随机读写带来的低效磁盘 IO；</li><li>consolidate 根据 CPU 的个数来决定每个 task shuffle map 端产生多少个文件，假设原来有 10 个 task，100 个 reduce，每个节点有 10 个 CPU，那么使用 hash shuffle 会产生 <code>10*100=1000</code> 个文件，consolidate 产生 <code>10*10=100</code> 个文件</li></ul><p>​注意：consolidate 部分减少了文件和文件句柄，并行读很高的情况下（task 很多时）还是会很多文件。</p><h3 id="spark-default-parallelism-这个参数有什么意义，实际生产中如何设置？"><a href="#spark-default-parallelism-这个参数有什么意义，实际生产中如何设置？" class="headerlink" title="spark.default.parallelism 这个参数有什么意义，实际生产中如何设置？"></a>spark.default.parallelism 这个参数有什么意义，实际生产中如何设置？</h3><ul><li>参数用于设置每个 Stage 的默认 Task 数量。这个参数极为重要，如果不设置可能会直接影响你的 Spark 作业性能；</li><li>很多人都不会设置这个参数，会使得集群非常低效，你的 CPU，内存再多，如果 Task 始终为 1，那也是浪费，Spark 官网建议 Task 个数为 <code>CPU 的核数 * Executor 的个数的 2~3 倍</code>。</li></ul><h3 id="spark-shuffle-memoryFraction-参数的含义，以及优化经验？"><a href="#spark-shuffle-memoryFraction-参数的含义，以及优化经验？" class="headerlink" title="spark.shuffle.memoryFraction 参数的含义，以及优化经验？"></a>spark.shuffle.memoryFraction 参数的含义，以及优化经验？</h3><ul><li><code>spark.shuffle.memoryFraction</code> 是 shuffle 调优中 重要参数，shuffle 从上一个 task 拉去数据过来，要在 Executor 进行聚合操作，聚合操作时使用 Executor 内存的比例由该参数决定，默认是 20%，如果聚合时数据超过了该大小，那么就会 spill 到磁盘，极大降低性能；</li><li>如果 Spark 作业中的 RDD 持久化操作较少，shuffle 操作较多时，建议降低持久化操作的内存占比，提高 shuffle 操作的内存占比比例，避免 shuffle 过程中数据过多时内存不够用，必须溢写到磁盘上，降低了性能。此外，如果发现作业由于频繁的 GC 导致运行缓慢，意味着 task 执行用户代码的内存不够用，那么同样建议调低这个参数的值。</li></ul><h3 id="Spark-中-Standalone-模式特点，有哪些优点和缺点？"><a href="#Spark-中-Standalone-模式特点，有哪些优点和缺点？" class="headerlink" title="Spark 中 Standalone 模式特点，有哪些优点和缺点？"></a>Spark 中 Standalone 模式特点，有哪些优点和缺点？</h3><ul><li>特点：<ul><li>Standalone 是 Master/Slave 架构，集群由 Master 与 Worker 节点组成，程序通过与 Master 节点交互申请资源，Worker 节点启动 Executor 运行；</li><li>Standalone 调度模式使用 FIFO 调度方式；</li><li>无依赖任何其他资源管理系统，Master 负责管理集群资源</li></ul></li><li>优点：<ul><li>部署简单；</li><li>不依赖其他资源管理系统。</li></ul></li><li>缺点：<ul><li>默认每个应用程序会独占所有可用节点的资源，当然可以通过 <code>spark.cores.max</code> 来决定一个应用可以申请的 CPU cores 个数；</li><li>可能有单点故障，需要自己配置 master HA。</li></ul></li></ul><h3 id="FIFO-调度模式的基本原理、优点和缺点？"><a href="#FIFO-调度模式的基本原理、优点和缺点？" class="headerlink" title="FIFO 调度模式的基本原理、优点和缺点？"></a>FIFO 调度模式的基本原理、优点和缺点？</h3><p>​基本原理：按照先后顺序决定资源的使用，资源优先满足最先来的 job。第一个 job 优先获取所有可用的资源，接下来第二个 job 再获取剩余资源。以此类推，如果第一个 job 没有占用所有的资源，那么第二个 job 还可以继续获取剩余资源，这样多个 job 可以并行运行，如果第一个 job 很大，占用所有资源，则第二个 job 就需要等待，等到第一个 job 释放所有资源。</p><p>​优点和缺点：</p><ul><li>适合长作业，不适合短作业；</li><li>适合 CPU 繁忙型作业（计算时间长，相当于长作业），不利于 IO 繁忙型作业（计算时间短，相当于短作业）。</li></ul><h3 id="FAIR-调度模式的优点和缺点？"><a href="#FAIR-调度模式的优点和缺点？" class="headerlink" title="FAIR 调度模式的优点和缺点？"></a>FAIR 调度模式的优点和缺点？</h3><p>​所有的任务拥有大致相当的优先级来共享集群资源，Spark 多以轮询的方式为任务分配资源，不管长任务还是短任务都可以获得资源，并且获得不错的响应时间，对于短任务，不会像 FIFO 那样等待较长时间了，通过参数 <code>spark.scheduler.mode</code> 为 FAIR 指定。</p><h3 id="CAPACITY-调度模式的优点和缺点？"><a href="#CAPACITY-调度模式的优点和缺点？" class="headerlink" title="CAPACITY 调度模式的优点和缺点？"></a>CAPACITY 调度模式的优点和缺点？</h3><ul><li><p>原理：</p><p>计算能力调度器支持多个队列，每个队列可配置一定的资源量，每个队列采用 FIFO 调度策略，为了防止同一个用户的作业独占队列中的资源，该调度器会对同一用户提交的作业所占资源量进行限定。调度时，首先按以下策略选择一个合适队列：计算每个队列中正在运行的任务数与其应该分得的计算资源之间的比值（即比较空闲的队列），选择一个该比值最小的队列；然后按以下策略选择该队列中一个作业：按照作业优先级和提交时间顺序选择，同时考虑用户资源量限制和内存限制</p></li><li>优点：<ul><li>计算能力保证：支持多个队列，某个作业可被提交到某一个队列中。每个队列会配置一定比例的计算资源，且所有提交到队列中的作业共享该队列中的资源；</li><li>灵活性：空闲资源会被分配给那些未达到资源使用上限的队列，当某个未达到资源的队列需要资源时，一旦出现空闲资源资源，便会分配给他们；</li><li>支持优先级：队列支持作业优先级调度（默认是 FIFO）；</li><li>多重租赁：综合考虑多种约束防止单个作业、用户或者队列独占队列或者集群中的资源；</li><li>基于资源的调度：支持资源密集型作业，允许作业使用的资源量高于默认值，进而可容纳不同资源需求的作业。不过，当前仅支持内存资源的调度。</li></ul></li></ul><h3 id="常见的数据压缩方式，你们生产集群采用了什么压缩方式，提升了多少效率？"><a href="#常见的数据压缩方式，你们生产集群采用了什么压缩方式，提升了多少效率？" class="headerlink" title="常见的数据压缩方式，你们生产集群采用了什么压缩方式，提升了多少效率？"></a>常见的数据压缩方式，你们生产集群采用了什么压缩方式，提升了多少效率？</h3><ul><li>数据压缩，大片连续区域进行数据存储并且存储区域中数据重复性高的状况下，可以使用适当的压缩算法。数组，对象序列化后都可以使用压缩，数更紧凑，减少空间开销。常见的压缩方式有 snappy，LZO，gz 等</li><li>Hadoop 生产环境常用的是 snappy 压缩方式（使用压缩，实际上是 CPU 换 IO 吞吐量和磁盘空间，所以如果 CPU 利用率不高，不忙的情况下，可以大大提升集群处理效率）。snappy 压缩比一般 20%~30% 之间，并且压缩和解压缩效率也非常高：<ul><li>GZIP 的压缩率最高，但是其实 CPU 密集型的，对 CPU 的消耗比其他算法要多，压缩和解压速度也慢；</li><li>LZO 的压缩率居中，比 GZIP 要低一些，但是压缩和解压速度明显要比 GZIP 快很多，其中解压速度快的更多；</li><li>Zippy/Snappy 的压缩率最低，而压缩和解压速度要稍微比 LZO 要快一些。</li></ul></li><li>提升了多少效率可以从两个方面回答：<ul><li>数据存储节约多少存储，</li><li>任务执行消耗时间节约了多少，可以举个实际例子展开描述。</li></ul></li></ul><h3 id="使用-scala-代码实现-WordCount？"><a href="#使用-scala-代码实现-WordCount？" class="headerlink" title="使用 scala 代码实现 WordCount？"></a>使用 scala 代码实现 WordCount？</h3><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">​<span class="keyword">val</span> conf = <span class="keyword">new</span> <span class="type">SparkConf</span>()</span><br><span class="line">​<span class="keyword">val</span> sc = <span class="keyword">new</span> <span class="type">SparkContext</span>(conf)</span><br><span class="line">​<span class="keyword">val</span> line = sc.textFile(<span class="string">&quot;xxxx.txt&quot;</span>)</span><br><span class="line">line.flatMap(_.split(<span class="string">&quot; &quot;</span>)).map((_,<span class="number">1</span>)).reduceByKey(_+_).collect().foreach(println)</span><br><span class="line">sc.stop()</span><br></pre></td></tr></table></figure></p><h3 id="Spark-RDD-和-MapReduce2-的区别？"><a href="#Spark-RDD-和-MapReduce2-的区别？" class="headerlink" title="Spark RDD 和 MapReduce2 的区别？"></a>Spark RDD 和 MapReduce2 的区别？</h3><ul><li>MR2 只有 2 个阶段，数据需要大量访问磁盘，数据来源相对单一，Spark RDD，可以无数个阶段进行迭代计算，数据来源非常丰富，数据落地介质也非常丰富 Spark 计算基于内存；</li><li>MR2 需要频繁操作磁盘 IO，需要大家明确的是，如果是 SparkRDD 的话，你要知道每一种数据来源对应的是什么，RDD 从数据源加载数据，将数据放到不同的 partition 针对这些 partition 中的数据进行迭代式计算计算完成之后，落地到不同的介质当中。</li></ul><h3 id="Spark-和-MapReduce-快？为什么快呢？快在哪里呢？"><a href="#Spark-和-MapReduce-快？为什么快呢？快在哪里呢？" class="headerlink" title="Spark 和 MapReduce 快？为什么快呢？快在哪里呢？"></a>Spark 和 MapReduce 快？为什么快呢？快在哪里呢？</h3><p>​Spark 更加快的主要原因有几点：</p><ul><li>基于内存计算，减少低效的磁盘交互；</li><li>高效的调度算法，基于 DAG；</li><li>容错机制 Lineage，主要是 DAG 和 Lineage，即使 Spark 不使用内存技术，也大大快于 MapReduce。</li></ul><h3 id="Spark-SQL-为什么比-Hive-快呢？"><a href="#Spark-SQL-为什么比-Hive-快呢？" class="headerlink" title="Spark SQL 为什么比 Hive 快呢？"></a>Spark SQL 为什么比 Hive 快呢？</h3><p>​计算引擎不一样，一个是 Spark 计算模型，一个是 MapReduce 计算模型。</p><h3 id="RDD-的数据结构是怎么样的？"><a href="#RDD-的数据结构是怎么样的？" class="headerlink" title="RDD 的数据结构是怎么样的？"></a>RDD 的数据结构是怎么样的？</h3><p>一个 RDD 对象，包含如下 5 个核心属性。</p><ul><li>一个分区列表，每个分区里是 RDD 的部分数据（或称数据块）。</li><li>一个依赖列表，存储依赖的其他 RDD。</li><li>一个名为 compute 的计算函数，用于计算 RDD 各分区的值。</li><li>分区器（可选），用于键/值类型的 RDD，比如某个 RDD 是按散列来分区。</li><li>计算各分区时优先的位置列表（可选），比如从 HDFS 上的文件生成 RDD 时，RDD 分区的位置优先选择数据所在的节点，这样可以避免数据移动带来的开销。</li></ul><h3 id="RDD-算子里操作一个外部-map，比如往里面-put-数据，然后算子外再遍历-map，会有什么问题吗？"><a href="#RDD-算子里操作一个外部-map，比如往里面-put-数据，然后算子外再遍历-map，会有什么问题吗？" class="headerlink" title="RDD 算子里操作一个外部 map，比如往里面 put 数据，然后算子外再遍历 map，会有什么问题吗？"></a>RDD 算子里操作一个外部 map，比如往里面 put 数据，然后算子外再遍历 map，会有什么问题吗？</h3><p>​频繁创建额外对象，容易 OOM。</p><h3 id="HBase-region-多大会分区，Spark-读取-HBase-数据是如何划分-partition-的？"><a href="#HBase-region-多大会分区，Spark-读取-HBase-数据是如何划分-partition-的？" class="headerlink" title="HBase region 多大会分区，Spark 读取 HBase 数据是如何划分 partition 的？"></a>HBase region 多大会分区，Spark 读取 HBase 数据是如何划分 partition 的？</h3><p>​region 超过了 <code>hbase.hregion.max.filesize</code> 这个参数配置的大小就会自动裂分，默认值是 1G。</p><p>​默认情况下，HBase 有多少个 region，Spark 读取时就会有多少个 partition</p><h2 id="Spark-调优"><a href="#Spark-调优" class="headerlink" title="Spark 调优"></a>Spark 调优</h2><h3 id="数据倾斜"><a href="#数据倾斜" class="headerlink" title="数据倾斜"></a>数据倾斜</h3><h3 id="什么是数据倾斜？"><a href="#什么是数据倾斜？" class="headerlink" title="什么是数据倾斜？"></a>什么是数据倾斜？</h3><p>数据倾斜指的是，并行处理的数据集中，某一部分（如 Spark 或 Kafka 的一个 Partition）的数据显著多于其它部分，从而使得该部分的处理速度成为整个数据集处理的瓶颈。</p><p>数据倾斜俩大直接致命后果。</p><ul><li>数据倾斜直接会导致一种情况：Out Of Memory。</li><li>运行速度慢。</li></ul><p>主要是发生在 Shuffle 阶段。同样 Key 的数据条数太多了。导致了某个 Key（下图中的 80 亿条）所在的 Task 数据量太大了。远远超过其他 Task 所处理的数据量。</p><p>一个经验结论是：<strong>一般情况下，OOM 的原因都是数据倾斜</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/spark_skew.png" alt="数据倾斜"></p><h3 id="如何定位数据倾斜？"><a href="#如何定位数据倾斜？" class="headerlink" title="如何定位数据倾斜？"></a>如何定位数据倾斜？</h3><p>数据倾斜一般会发生在 shuffle 过程中。很大程度上是你使用了可能会触发 shuffle 操作的算子：distinct、groupByKey、reduceByKey、aggregateByKey、join、cogroup、repartition 等。</p><p>原因：查看任务 -&gt; 查看 Stage -&gt; 查看代码</p><ul><li>某个 task 执行特别慢的情况</li><li>某个 task 莫名其妙内存溢出的情况</li><li>查看导致数据倾斜的 key 的数据分布情况</li><li>是不是有 OOM 情况出现，一般是少数内存溢出的问题</li><li>是不是应用运行时间差异很大，总体时间很长</li><li>需要了解你所处理的数据 Key 的分布情况，如果有些 Key 有大量的条数，那么就要小心数据倾斜的问题</li><li>一般需要通过 Spark Web UI 和其他一些监控方式出现的异常来综合判断</li><li>看看代码里面是否有一些导致 Shuffle 的算子出现</li></ul><h3 id="数据倾斜的几种典型情况？"><a href="#数据倾斜的几种典型情况？" class="headerlink" title="数据倾斜的几种典型情况？"></a>数据倾斜的几种典型情况？</h3><ol><li>数据源中的数据分布不均匀，Spark 需要频繁交互</li><li>数据集中的不同 Key 由于分区方式，导致数据倾斜</li><li>JOIN 操作中，一个数据集中的数据分布不均匀，另一个数据集较小（主要）</li><li>聚合操作中，数据集中的数据分布不均匀（主要）</li><li>JOIN 操作中，两个数据集都比较大，其中只有几个 Key 的数据分布不均匀</li><li>JOIN 操作中，两个数据集都比较大，有很多 Key 的数据分布不均匀</li><li>数据集中少数几个 key 数据量很大，不重要，其他数据均匀</li></ol><p><strong>注意：</strong></p><ul><li>需要处理的数据倾斜问题就是 Shuffle 后数据的分布是否均匀问题</li><li>只要保证最后的结果是正确的，可以采用任何方式来处理数据倾斜，只要保证在处理过程中不发生数据倾斜就可以</li></ul><h3 id="数据倾斜的处理方法？"><a href="#数据倾斜的处理方法？" class="headerlink" title="数据倾斜的处理方法？"></a>数据倾斜的处理方法？</h3><ol><li><p>数据源中的数据分布不均匀，Spark 需要频繁交互</p><p>解决方案：避免数据源的数据倾斜。</p><p>实现原理：通过在 Hive 中对倾斜的数据进行预处理，以及在进行 Kafka 数据分发时尽量进行平均分配。这种方案从根源上解决了数据倾斜，彻底避免了在 Spark 中执行 shuffle 类算子，那么肯定就不会有数据倾斜的问题了。</p><p>方案优点：实现起来简单便捷，效果还非常好，完全规避掉了数据倾斜，Spark 作业的性能会大幅度提升。</p><p>方案缺点：治标不治本，Hive 或者 Kafka 中还是会发生数据倾斜。</p><p>适用情况：在一些 Java 系统与 Spark 结合使用的项目中，会出现 Java 代码频繁调用 Spark 作业的场景，而且对 Spark 作业的执行性能要求很高，就比较适合使用这种方案。将数据倾斜提前到上游的 Hive ETL，每天仅执行一次，只有那一次是比较慢的，而之后每次 Java 调用 Spark 作业时，执行速度都会很快，能够提供更好的用户体验。</p><p>总结：前台的 Java 系统和 Spark 有很频繁的交互，这个时候如果 Spark 能够在最短的时间内处理数据，往往会给前端有非常好的体验。这个时候可以将数据倾斜的问题抛给数据源端，在数据源端进行数据倾斜的处理。但是这种方案没有真正的处理数据倾斜问题。</p></li><li><p>数据集中的不同 Key 由于分区方式，导致数据倾斜</p><p>解决方案一：调整并行度。</p><p>实现原理：增加 shuffle read task 的数量，可以让原本分配给一个 task 的多个 key 分配给多个 task，从而让每个 task 处理比原来更少的数据。</p><p>方案优点：实现起来比较简单，可以有效缓解和减轻数据倾斜的影响。</p><p>方案缺点：只是缓解了数据倾斜而已，没有彻底根除问题，根据实践经验来看，其效果有限。</p><p>实践经验：该方案通常无法彻底解决数据倾斜，因为如果出现一些极端情况，比如某个 key 对应的数据量有 100 万，那么无论你的 task 数量增加到多少，都无法处理。</p><p>总结：调整并行度：适合于有大量 key 由于分区算法或者分区数的问题，将 key 进行了不均匀分区，可以通过调大或者调小分区数来试试是否有效。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/spark_shuffle_read_task.png" alt="shuffle read task"></p><p>解决方案二：缓解数据倾斜（自定义 Partitioner）。</p><p>适用场景：大量不同的 Key 被分配到了相同的 Task 造成该 Task 数据量过大。</p><p>解决方案：使用自定义的 Partitioner 实现类代替默认的 HashPartitioner，尽量将所有不同的 Key 均匀分配到不同的 Task 中。</p><p>方案优点：不影响原有的并行度设计。如果改变并行度，后续 Stage 的并行度也会默认改变，可能会影响后续 Stage。</p><p>方案缺点：适用场景有限，只能将不同 Key 分散开，对于同一 Key 对应数据集非常大的场景不适用。效果与调整并行度类似，只能缓解数据倾斜而不能完全消除数据倾斜。而且需要根据数据特点自定义专用的 Partitioner，不够灵活。</p></li><li><p>JOIN 操作中，一个数据集中的数据分布不均匀，另一个数据集较小（主要）</p><p>解决方案：Reduce side Join 转变为 Map side Join。</p><p>适用场景：在对 RDD 使用 join 类操作，或者是在 Spark SQL 中使用 join 语句时，而且 join 操作中的一个 RDD 或表的数据量比较小（比如几百兆），比较适用此方案。</p><p>实现原理：普通的 join 是会走 shuffle 过程的，而一旦 shuffle，就相当于会将相同 key 的数据拉取到一个 shuffle read task 中再进行 join，此时就是 reduce join。但是如果一个 RDD 是比较小的，则可以采用广播小 RDD 全量数据 + map 算子来实现与 join 同样的效果，也就是 map join，此时就不会发生 shuffle 操作，也就不会发生数据倾斜。</p><p>方案优点：对 join 操作导致的数据倾斜，效果非常好，因为根本就不会发生 shuffle，也就根本不会发生数据倾斜。</p><p>方案缺点：适用场景较少，因为这个方案只适用于一个大表和一个小表的情况。</p></li><li><p>聚合操作中，数据集中的数据分布不均匀（主要）</p><p>解决方案：两阶段聚合（局部聚合 + 全局聚合）。</p><p>适用场景：对 RDD 执行 reduceByKey 等聚合类 shuffle 算子或者在 Spark SQL 中使用 group by 语句进行分组聚合时，比较适用这种方案。</p><p>实现原理：将原本相同的 key 通过附加随机前缀的方式，变成多个不同的 key，就可以让原本被一个 task 处理的数据分散到多个 task 上去做局部聚合，进而解决单个 task 处理数据量过多的问题。接着去除掉随机前缀，再次进行全局聚合，就可以得到最终的结果。具体原理见下图。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/spark_prefix.png" alt="附加随机前缀两阶段聚合"></p><p>方案优点：对于聚合类的 shuffle 操作导致的数据倾斜，效果是非常不错的。通常都可以解决掉数据倾斜，或者至少是大幅度缓解数据倾斜，将 Spark 作业的性能提升数倍以上。</p><p>方案缺点：仅仅适用于聚合类的 shuffle 操作，适用范围相对较窄。如果是 join 类的 shuffle 操作，还得用其他的解决方案，将相同 key 的数据分拆处理。</p></li><li><p>JOIN 操作中，两个数据集都比较大，其中只有几个 Key 的数据分布不均匀</p><p>解决方案：为倾斜 key 增加随机前/后缀。</p><p>适用场景：两张表都比较大，无法使用 Map 侧 Join。其中一个 RDD 有少数几个 Key 的数据量过大，另外一个 RDD 的 Key 分布较为均匀。</p><p>解决方案：将有数据倾斜的 RDD 中倾斜 Key 对应的数据集单独抽取出来加上随机前缀，另外一个 RDD 每条数据分别与随机前缀结合形成新的 RDD（笛卡尔积，相当于将其数据增到到原来的 N 倍，N 即为随机前缀的总个数），然后将二者 Join 后去掉前缀。然后将不包含倾斜 Key 的剩余数据进行 Join。最后将两次 Join 的结果集通过 union 合并，即可得到全部 Join 结果。</p><p>方案优点：相对于 Map 侧 Join，更能适应大数据集的 Join。如果资源充足，倾斜部分数据集与非倾斜部分数据集可并行进行，效率提升明显。且只针对倾斜部分的数据做数据扩展，增加的资源消耗有限。</p><p>方案缺点：如果倾斜 Key 非常多，则另一侧数据膨胀非常大，此方案不适用。而且此时对倾斜 Key 与非倾斜 Key 分开处理，需要扫描数据集两遍，增加了开销。</p><p>注意：具有倾斜 Key 的 RDD 数据集中，key 的数量比较少。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/spark_prefix_suffix.png" alt="为倾斜 key 增加随机前/后缀"></p></li><li><p>JOIN 操作中，两个数据集都比较大，有很多 Key 的数据分布不均匀</p><p>解决方案：随机前缀和扩容 RDD 进行 join。</p><p>适用场景：如果在进行 join 操作时，RDD 中有大量的 key 导致数据倾斜，那么进行分拆 key 也没什么意义。</p><p>实现思路：将该 RDD 的每条数据都打上一个 N 以内的随机前缀。同时对另外一个正常的 RDD 进行扩容，将每条数据都扩容成 N 条数据，扩容出来的每条数据都依次打上一个 1~N 的前缀。最后将两个处理后的 RDD 进行 join 即可。和上一种方案是尽量只对少数倾斜 key 对应的数据进行特殊处理，由于处理过程需要扩容 RDD，因此上一种方案扩容 RDD 后对内存的占用并不大；而这一种方案是针对有大量倾斜 key 的情况，没法将部分 key 拆分出来进行单独处理，因此只能对整个 RDD 进行数据扩容，对内存资源要求很高。</p><p>方案优点：对 join 类型的数据倾斜基本都可以处理，而且效果也相对比较显著，性能提升效果非常不错。</p><p>方案缺点：该方案更多的是缓解数据倾斜，而不是彻底避免数据倾斜。而且需要对整个 RDD 进行扩容，对内存资源要求很高。</p><p>实践经验：曾经开发一个数据需求的时候，发现一个 join 导致了数据倾斜。优化之前，作业的执行时间大约是 60 分钟左右；使用该方案优化之后，执行时间缩短到 10 分钟左右，性能提升了 6 倍。</p><p>注意：将倾斜 Key 添加 1-N 的随机前缀，并将被 Join 的数据集相应的扩大 N 倍（需要将 1-N 数字添加到每一条数据上作为前缀）</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/spark_prefix_join.png" alt="随机前缀和扩容 RDD 进行 join"></p></li><li><p>数据集中少数几个 key 数据量很大，不重要，其他数据均匀</p><p>解决方案：过滤少数倾斜 Key。</p><p>适用场景：如果发现导致倾斜的 key 就少数几个，而且对计算本身的影响并不大的话，那么很适合使用这种方案。比如 99% 的 key 就对应 10 条数据，但是只有一个 key 对应了 100 万数据，从而导致了数据倾斜。</p><p>方案优点：实现简单，而且效果也很好，可以完全规避掉数据倾斜。</p><p>方案缺点：适用场景不多，大多数情况下，导致倾斜的 key 还是很多的，并不是只有少数几个。</p><p>实践经验：在项目中我们也采用过这种方案解决数据倾斜。有一次发现某一天 Spark 作业在运行的时候突然 OOM 了，追查之后发现，是 Hive 表中的某一个 key 在那天数据异常，导致数据量暴增。因此就采取每次执行前先进行采样，计算出样本中数据量最大的几个 key 之后，直接在程序中将那些 key 给过滤掉。</p></li></ol><h3 id="资源调优"><a href="#资源调优" class="headerlink" title="资源调优"></a>资源调优</h3><h3 id="资源运行中的集中情况"><a href="#资源运行中的集中情况" class="headerlink" title="资源运行中的集中情况"></a>资源运行中的集中情况</h3><ul><li>实践中跑的 Spark job，有的特别慢，查看 CPU 利用率很低，可以尝试减少每个 executor 占用 CPU core 的数量，增加并行的 executor 数量，同时配合增加分片，整体上增加了 CPU 的利用率，加快数据处理速度。</li><li>发现某 job 很容易发生内存溢出，我们就增大分片数量，从而减少了每片数据的规模，同时还减少并行的 executor 数量，这样相同的内存资源分配给数量更少的 executor，相当于增加了每个 task 的内存分配，这样运行速度可能慢了些，但是总比 OOM 强。</li><li>数据量特别少，有大量的小文件生成，就减少文件分片，没必要创建那么多 task，这种情况，如果只是最原始的 input 比较小，一般都能被注意到；但是，如果是在运算过程中，比如应用某个 reduceBy 或者某个 filter 以后，数据大量减少，这种低效情况就很少被留意到。</li></ul><h3 id="运行资源优化配置"><a href="#运行资源优化配置" class="headerlink" title="运行资源优化配置"></a>运行资源优化配置</h3><p>一个 CPU core 同一时间只能执行一个线程。而每个 Executor 进程上分配到的多个 task，都是以每个 task 一条线程的方式，多线程并发运行的。</p><p>一个应用提交的时候设置多大的内存？设置多少 Core？ 设置几个 Executor？</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">./bin/spark-submit \</span><br><span class="line">  --master yarn-cluster \</span><br><span class="line">  --num-executors 100 \</span><br><span class="line">  --executor-memory 6G \</span><br><span class="line">  --executor-cores 4 \</span><br><span class="line">  --driver-memory 1G \</span><br><span class="line">  --conf spark.default.parallelism=1000 \</span><br><span class="line">  --conf spark.storage.memoryFraction=0.5 \</span><br><span class="line">  --conf spark.shuffle.memoryFraction=0.3</span><br></pre></td></tr></table></figure></p><ol><li><p><code>-num-executors</code></p><ul><li><p>参数说明：该参数用于设置 Spark 作业总共要用多少个 Executor 进程来执行。Driver 在向 YARN 集群管理器申请资源时，YARN 集群管理器会尽可能按照你的设置来在集群的各个工作节点上，启动相应数量的 Executor 进程。这个参数非常之重要，如果不设置的话，默认只会给你启动少量的 Executor 进程，此时你的 Spark 作业的运行速度是非常慢的。</p></li><li><p>调优建议：每个 Spark 作业的运行一般设置 50~100 个左右的 Executor 进程比较合适，设置太少或太多的 Executor 进程都不好。设置的太少，无法充分利用集群资源；设置的太多的话，大部分队列可能无法给予充分的资源。</p></li></ul></li><li><p><code>-executor-memory</code></p><ul><li><p>参数说明：该参数用于设置每个 Executor 进程的内存。Executor 内存的大小，很多时候直接决定了 Spark 作业的性能，而且跟常见的 JVM OOM 异常，也有直接的关联。</p></li><li><p>调优建议：每个 Executor 进程的内存设置 4G~8G 较为合适。但是这只是一个参考值，具体的设置还是得根据不同部门的资源队列来定。可以看看自己团队的资源队列的最大内存限制是多少，<code>num-executors * executor-memory</code>，是不能超过队列的最大内存量的。此外，如果你是跟团队里其他人共享这个资源队列，那么申请的内存量最好不要超过资源队列最大总内存的 1/3~1/2，避免你自己的 Spark 作业占用了队列所有的资源，导致别的同事的作业无法运行。</p></li></ul></li><li><p><code>-executor-cores</code></p><ul><li><p>参数说明：该参数用于设置每个 Executor 进程的 CPU core 数量。这个参数决定了每个 Executor 进程并行执行 task 线程的能力。因为每个 CPU core 同一时间只能执行一个 task 线程，因此每个 Executor 进程的 CPU core 数量越多，越能够快速地执行完分配给自己的所有 task 线程。</p></li><li><p>调优建议：Executor 的 CPU core 数量设置为 2~4 个较为合适。同样得根据不同部门的资源队列来定，可以看看自己的资源队列的最大 CPU core 限制是多少，再依据设置的 Executor 数量，来决定每个 Executor 进程可以分配到几个 CPU core。同样建议，如果是跟他人共享这个队列，那么 <code>num-executors * executor-cores</code> 不要超过队列总 CPU core 的 1/3~1/2 左右比较合适，也是避免影响其他同事的作业运行。</p></li></ul></li><li><p><code>-driver-memory</code></p><ul><li><p>参数说明：该参数用于设置 Driver 进程的内存。</p></li><li><p>调优建议：Driver 的内存通常来说不设置，或者设置 1G 左右应该就够了。唯一需要注意的一点是，如果需要使用 collect 算子将 RDD 的数据全部拉取到 Driver 上进行处理（或者是用 map side join 操作），那么必须确保 Driver 的内存足够大，否则会出现 OOM 内存溢出的问题。</p></li></ul></li><li><p><code>-spark.default.parallelism</code></p><ul><li><p>参数说明：该参数用于设置每个 stage 的默认 task 数量，也可以认为是分区数。这个参数极为重要，如果不设置可能会直接影响你的 Spark 作业性能。</p></li><li><p>调优建议：Spark 作业的默认 task 数量为 500~1000 个较为合适。很多人常犯的一个错误就是不去设置这个参数，那么此时就会导致 Spark 自己根据底层 HDFS 的 block 数量来设置 task 的数量，默认是一个 HDFS block 对应一个 task。通常来说，Spark 默认设置的数量是偏少的（比如就几十个 task），如果 task 数量偏少的话，就会导致你前面设置好的 Executor 的参数都前功尽弃。试想一下，无论你的 Executor 进程有多少个，内存和 CPU 有多大，但是 task 只有 1 个或者 10 个，那么 90% 的 Executor 进程可能根本就没有 task 执行，也就是白白浪费了资源！因此 Spark 官网建议的设置原则是，设置该参数为 <code>num-executors * executor-cores</code> 的 2~3 倍较为合适，比如 Executor 的总 CPU core 数量为 300 个，那么设置 1000 个 task 是可以的，此时可以充分地利用 Spark 集群的资源。</p></li></ul></li><li><p><code>-spark.storage.memoryFraction</code></p><ul><li><p>参数说明：该参数用于设置 RDD 持久化数据在 Executor 内存中能占的比例，默认是 0.6。也就是说，默认 Executor 60% 的内存，可以用来保存持久化的 RDD 数据。根据你选择的不同的持久化策略，如果内存不够时，可能数据就不会持久化，或者数据会写入磁盘。</p></li><li><p>调优建议：如果 Spark 作业中，有较多的 RDD 持久化操作，该参数的值可以适当提高一些，保证持久化的数据能够容纳在内存中。避免内存不够缓存所有的数据，导致数据只能写入磁盘中，降低了性能。但是如果 Spark 作业中的 shuffle 类操作比较多，而持久化操作比较少，那么这个参数的值适当降低一些比较合适。此外，如果发现作业由于频繁的 GC 导致运行缓慢（通过 spark web ui 可以观察到作业的 GC 耗时），意味着 task 执行用户代码的内存不够用，那么同样建议调低这个参数的值。</p></li></ul></li><li><p><code>-spark.shuffle.memoryFraction</code></p><ul><li><p>参数说明：该参数用于设置 shuffle 过程中一个 task 拉取到上个 stage 的 task 的输出后，进行聚合操作时能够使用的 Executor 内存的比例，默认是 0.2。也就是说，Executor 默认只有 20% 的内存用来进行该操作。shuffle 操作在进行聚合时，如果发现使用的内存超出了这个 20% 的限制，那么多余的数据就会溢写到磁盘文件中去，此时就会极大地降低性能。</p></li><li><p>调优建议：如果 Spark 作业中的 RDD 持久化操作较少，shuffle 操作较多时，建议降低持久化操作的内存占比，提高 shuffle 操作的内存占比比例，避免 shuffle 过程中数据过多时内存不够用，必须溢写到磁盘上，降低了性能。此外，如果发现作业由于频繁的 GC 导致运行缓慢，意味着 task 执行用户代码的内存不够用，那么同样建议调低这个参数的值。</p></li></ul></li></ol><p>总结：</p><ul><li><code>num-executors</code>：应用运行时 executor 的数量，推荐 50-100 左右比较合适</li><li><code>executor-memory</code>：应用运行时 executor 的内存，推荐 4-8G 比较合适</li><li><code>executor-cores</code>：应用运行时 executor 的 CPU 核数，推荐 2-4 个比较合适</li><li><code>driver-memory</code>：应用运行时 driver 的内存量，主要考虑如果使用 map side join 或者一些类似于 collect 的操作，那么要相应调大内存量</li><li><code>spark.default.parallelism</code>：每个 stage 默认的 task 数量，推荐参数为 <code>num-executors * executor-cores</code> 的 2~3 倍较为合适</li><li><code>spark.storage.memoryFraction</code>：每一个 executor 中用于 RDD 缓存的内存比例，如果程序中有大量的数据缓存，可以考虑调大整个的比例，默认为 60%</li><li><code>spark.shuffle.memoryFraction</code>：每一个 executor 中用于 Shuffle 操作的内存比例，默认是 20%，如果程序中有大量的 Shuffle 类算子，那么可以考虑其它的比例</li></ul><h3 id="程序开发调优"><a href="#程序开发调优" class="headerlink" title="程序开发调优"></a>程序开发调优</h3><h3 id="避免创建重复的-RDD"><a href="#避免创建重复的-RDD" class="headerlink" title="避免创建重复的 RDD"></a>避免创建重复的 RDD</h3><p>需要对名为 <code>hello.txt</code> 的 HDFS 文件进行一次 map 操作，再进行一次 reduce 操作。也就是说，需要对一份数据执行两次算子操作。</p><p>错误的做法：对于同一份数据执行多次算子操作时，创建多个 RDD。这里执行了两次 textFile 方法，针对同一个 HDFS 文件，创建了两个 RDD 出来，然后分别对每个 RDD 都执行了一个算子操作。这种情况下，Spark 需要从 HDFS 上两次加载 <code>hello.txt</code> 文件的内容，并创建两个单独的 RDD；// 第二次加载 HDFS 文件以及创建 RDD 的性能开销，很明显是白白浪费掉的。</p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> rdd1 = sc.textFile(<span class="string">&quot;hdfs://master:9000/hello.txt&quot;</span>)</span><br><span class="line">rdd1.map(...)</span><br><span class="line"><span class="keyword">val</span> rdd2 = sc.textFile(<span class="string">&quot;hdfs://master:9000/hello.txt&quot;</span>)</span><br><span class="line">rdd2.reduce(...)</span><br></pre></td></tr></table></figure></p><p>正确的用法：对于一份数据执行多次算子操作时，只使用一个 RDD。</p><h3 id="尽可能复用同一个-RDD"><a href="#尽可能复用同一个-RDD" class="headerlink" title="尽可能复用同一个 RDD"></a>尽可能复用同一个 RDD</h3><p>错误的做法：有一个 <code>&lt;long , String&gt;</code> 格式的 RDD，即 rdd1。接着由于业务需要，对 rdd1 执行了一个 map 操作，创建了一个 rdd2，而 rdd2 中的数据仅仅是 rdd1 中的 value 值而已，也就是说，rdd2 是 rdd1 的子集。</p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">JavaPairRDD</span>&lt;long , <span class="type">String</span>&gt; rdd1 = ...</span><br><span class="line"><span class="type">JavaRDD</span>&lt;string&gt; rdd2 = rdd1.map(...)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 分别对 rdd1 和 rdd2 执行了不同的算子操作。</span></span><br><span class="line">rdd1.reduceByKey(...)</span><br><span class="line">rdd2.map(...)</span><br></pre></td></tr></table></figure></p><p>正确的做法：rdd2 的数据完全就是 rdd1 的子集而已，却创建了两个 rdd，并对两个 rdd 都执行了一次算子操作。此时会因为对 rdd1 执行 map 算子来创建 rdd2，而多执行一次算子操作，进而增加性能开销。其实在这种情况下完全可以复用同一个 RDD。我们可以使用 rdd1，既做 reduceByKey 操作，也做 map 操作。</p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">JavaPairRDD</span>&lt;long , <span class="type">String</span>&gt; </span><br><span class="line">rdd1 = ...rdd1.reduceByKey(...)</span><br><span class="line">rdd1.map(tuple._2...)</span><br></pre></td></tr></table></figure></p><h3 id="对多次使用的-RDD-进行持久化"><a href="#对多次使用的-RDD-进行持久化" class="headerlink" title="对多次使用的 RDD 进行持久化"></a>对多次使用的 RDD 进行持久化</h3><p>正确的做法：cache 方法表示：使用非序列化的方式将 RDD 中的数据全部尝试持久化到内存中。此时再对 rdd1 执行两次算子操作时，只有在第一次执行 map 算子时，才会将这个 rdd1 从源头处计算一次。第二次执行 reduce 算子时，就会直接从内存中提取数据进行计算，不会重复计算一个 rdd。</p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> rdd1 = sc.textFile(<span class="string">&quot;hdfs://192.168.0.1:9000/hello.txt&quot;</span>).cache()</span><br><span class="line">rdd1.map(...)</span><br><span class="line">rdd1.reduce(...)</span><br></pre></td></tr></table></figure></p><p>序列化的方式可以减少持久化的数据对内存/磁盘的占用量，进而避免内存被持久化数据占用过多，从而发生频繁 GC。</p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> rdd1 = sc.textFile(<span class="string">&quot;hdfs://192.168.0.1:9000/hello.txt&quot;</span>).persist(<span class="type">StorageLevel</span>.<span class="type">MEMORY_AND_DISK_SER</span>)</span><br><span class="line">rdd1.map(...)</span><br><span class="line">rdd1.reduce(...)</span><br></pre></td></tr></table></figure></p><p>注意：通常不建议使用 <code>DISK_ONLY</code> 和后缀为 <code>_2</code> 的级别：因为完全基于磁盘文件进行数据的读写，会导致性能急剧降低，导致网络较大开销</p><h3 id="尽量避免使用-shuffle-类算子"><a href="#尽量避免使用-shuffle-类算子" class="headerlink" title="尽量避免使用 shuffle 类算子"></a>尽量避免使用 shuffle 类算子</h3><p>如果有可能的话，要尽量避免使用 shuffle 类算子，最消耗性能的地方就是 shuffle 过程。shuffle 过程中，各个节点上的相同 key 都会先写入本地磁盘文件中，然后其他节点需要通过网络传输拉取各个节点上的磁盘文件中的相同 key。而且相同 key 都拉取到同一个节点进行聚合操作时，还有可能会因为一个节点上处理的 key 过多，导致内存不够存放，进而溢写到磁盘文件中。因此在 shuffle 过程中，可能会发生大量的磁盘文件读写的 IO 操作，以及数据的网络传输操作。磁盘 IO 和网络数据传输也是 shuffle 性能较差的主要原因。</p><p>尽可能避免使用 reduceByKey、join、distinct、repartition 等会进行 shuffle 的算子，尽量使用 map 类的非 shuffle 算子。传统的 join 操作会导致 shuffle 操作。因为两个 RDD 中，相同的 key 都需要通过网络拉取到一个节点上，由一个 task 进行 join 操作。</p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> rdd3 = rdd1.join(rdd2)</span><br></pre></td></tr></table></figure></p><p>Broadcast + map 的 join 操作，不会导致 shuffle 操作。使用 Broadcast 将一个数据量较小的 RDD 作为广播变量。</p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> rdd2Data = rdd2.collect()</span><br><span class="line"><span class="keyword">val</span> rdd2DataBroadcast = sc.broadcast(rdd2Data)</span><br><span class="line"><span class="keyword">val</span> rdd3 = rdd1.map(rdd2DataBroadcast...)</span><br></pre></td></tr></table></figure></p><p>注意：以上操作，建议仅仅在 rdd2 的数据量比较少（比如几百 M，或者一两 G）的情况下使用。因为每个 Executor 的内存中，都会驻留一份 rdd2 的全量数据。</p><h3 id="使用-map-side-预聚合的-shuffle-操作"><a href="#使用-map-side-预聚合的-shuffle-操作" class="headerlink" title="使用 map-side 预聚合的 shuffle 操作"></a>使用 map-side 预聚合的 shuffle 操作</h3><p>如果因为业务需要，一定要使用 shuffle 操作，无法用 map 类的算子来替代，那么尽量使用可以 map-side 预聚合的算子，类似于 MapReduce 中的本地 combiner。map-side 预聚合之后，每个节点本地就只会有一条相同的 key，因为多条相同的 key 都被聚合起来了。其他节点在拉取所有节点上的相同 key 时，就会大大减少需要拉取的数据数量，从而也就减少了磁盘 IO 以及网络传输开销。</p><p>建议使用 reduceByKey 或者 aggregateByKey 算子来替代掉 groupByKey 算子</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/spark_map_side.png" alt="map-side 预聚合"></p><h3 id="使用高性能的算子"><a href="#使用高性能的算子" class="headerlink" title="使用高性能的算子"></a>使用高性能的算子</h3><ul><li>使用 reduceByKey/aggregateByKey 替代 groupByKey：map-side</li><li>使用 mapPartitions 替代普通 map：函数执行频率</li><li>使用 foreachPartitions 替代 foreach：函数执行频率</li><li>使用 filter 之后进行 coalesce 操作：filter 后对分区进行压缩</li><li>使用 repartitionAndSortWithinPartitions 替代 repartition 与 sort 类操作</li><li>repartitionAndSortWithinPartitions 是 Spark 官网推荐的一个算子，官方建议，如果需要在 repartition 重分区之后，还要进行排序，建议直接使用 repartitionAndSortWithinPartitions 算子</li></ul><h3 id="广播大变量"><a href="#广播大变量" class="headerlink" title="广播大变量"></a>广播大变量</h3><p>有时在开发过程中，会遇到需要在算子函数中使用外部变量的场景（尤其是大变量，比如 100M 以上的大集合），那么此时就应该使用 Spark 的广播（Broadcast）功能来提升性能。</p><p>默认情况下，Spark 会将该变量复制多个副本，通过网络传输到 task 中，此时每个 task 都有一个变量副本。如果变量本身比较大的话（比如 100M，甚至 1G），那么大量的变量副本在网络中传输的性能开销，以及在各个节点的 Executor 中占用过多内存导致的频繁 GC，都会极大地影响性能。</p><p>广播后的变量，会保证每个 Executor 的内存中，只驻留一份变量副本，而 Executor 中的 task 执行时共享该 Executor 中的那份变量副本。</p><h3 id="使用-Kryo-优化序列化性能"><a href="#使用-Kryo-优化序列化性能" class="headerlink" title="使用 Kryo 优化序列化性能"></a>使用 Kryo 优化序列化性能</h3><ul><li>在算子函数中使用到外部变量时，该变量会被序列化后进行网络传输。</li><li>将自定义的类型作为 RDD 的泛型类型时（比如 JavaRDD，Student 是自定义类型），所有自定义类型对象，都会进行序列化。因此这种情况下，也要求自定义的类必须实现 Serializable  接口。</li><li>使用可序列化的持久化策略时（比如 <code>MEMORY_ONLY_SER</code>），Spark 会将 RDD 中的每个 partition 都序列化成一个大的字节数组。</li></ul><p>Spark 默认使用的是 Java 的序列化机制，你可以使用 Kryo 作为序列化类库，效率要比 Java 的序列化机制要高</p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建 SparkConf 对象。</span></span><br><span class="line"><span class="keyword">val</span> conf = <span class="keyword">new</span> <span class="type">SparkConf</span>().setMaster(...).setAppName(...)</span><br><span class="line"><span class="comment">// 设置序列化器为 KryoSerializer。</span></span><br><span class="line">conf.set(<span class="string">&quot;spark.serializer&quot;</span>, <span class="string">&quot;org.apache.spark.serializer.KryoSerializer&quot;</span>)</span><br><span class="line"><span class="comment">// 注册要序列化的自定义类型。</span></span><br><span class="line">conf.registerKryoClasses(<span class="type">Array</span>(classOf[<span class="type">MyClass1</span>], classOf[<span class="type">MyClass2</span>]))</span><br></pre></td></tr></table></figure></p><h3 id="分区-Shuffle-优化"><a href="#分区-Shuffle-优化" class="headerlink" title="分区 Shuffle 优化"></a>分区 Shuffle 优化</h3><p>例如当遇到 userData 和 events 进行 join 时，userData 比较大，而且 join 操作比较频繁，这个时候，可以先将 userData 调用了 partitionBy() 分区，可以极大提高效率。</p><p>cogroup()、 groupWith()、join()、leftOuterJoin()、rightOuterJoin()、groupByKey()、reduceByKey()、 combineByKey() 以及 lookup() 等都能够受益</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/spark_shuffle_partition.png" alt="分区 Shuffle 优化"></p><p>总结：如果遇到一个 RDD 频繁和其他 RDD 进行 Shuffle 类操作，比如 cogroup()、 groupWith()、join()、leftOuterJoin()、rightOuterJoin()、groupByKey()、reduceByKey()、 combineByKey() 以及 lookup() 等，那么最好将该 RDD 通过 partitionBy() 操作进行预分区，这些操作在 Shuffle 过程中会减少 Shuffle 的数据量</p><h3 id="优化数据结构"><a href="#优化数据结构" class="headerlink" title="优化数据结构"></a>优化数据结构</h3><p>Java 中，有三种类型比较耗费内存：</p><ul><li>对象，每个 Java 对象都有对象头、引用等额外的信息，因此比较占用内存空间。</li><li>字符串，每个字符串内部都有一个字符数组以及长度等额外信息。</li><li>集合类型，比如 HashMap、LinkedList 等，因为集合类型内部通常会使用一些内部类来封装集合元素，比如 Map.Entry。</li></ul><p>Spark 官方建议，在 Spark 编码实现中，特别是对于算子函数中的代码，尽量不要使用上述三种数据结构，尽量使用字符串替代对象，使用原始类型（比如 Int、Long）替代字符串，使用数组替代集合类型，这样尽可能地减少内存占用，从而降低 GC 频率，提升性能。</p><h3 id="Shuffle-配置调优"><a href="#Shuffle-配置调优" class="headerlink" title="Shuffle 配置调优"></a>Shuffle 配置调优</h3><h3 id="spark-shuffle-file-buffer"><a href="#spark-shuffle-file-buffer" class="headerlink" title="spark.shuffle.file.buffer"></a><code>spark.shuffle.file.buffer</code></h3><ul><li><p>默认值：32k</p></li><li><p>参数说明：该参数用于设置 shuffle write task 的 BufferedOutputStream 的 buffer 缓冲大小。将数据写到磁盘文件之前，会先写入 buffer 缓冲中，待缓冲写满之后，才会溢写到磁盘。</p></li><li><p>调优建议：如果作业可用的内存资源较为充足的话，可以适当增加这个参数的大小（比如 64k），从而减少 shuffle write 过程中溢写磁盘文件的次数，也就可以减少磁盘 IO 次数，进而提升性能。在实践中发现，合理调节该参数，性能会有 1%~5% 的提升。</p></li></ul><h3 id="spark-reducer-maxSizeInFlight"><a href="#spark-reducer-maxSizeInFlight" class="headerlink" title="spark.reducer.maxSizeInFlight"></a><code>spark.reducer.maxSizeInFlight</code></h3><ul><li><p>默认值：48m</p></li><li><p>参数说明：该参数用于设置 shuffle read task 的 buffer 缓冲大小，而这个 buffer 缓冲决定了每次能够拉取多少数据。</p></li><li><p>调优建议：如果作业可用的内存资源较为充足的话，可以适当增加这个参数的大小（比如 96m），从而减少拉取数据的次数，也就可以减少网络传输的次数，进而提升性能。在实践中发现，合理调节该参数，性能会有 1%~5% 的提升。</p></li></ul><h3 id="spark-shuffle-io-maxRetries"><a href="#spark-shuffle-io-maxRetries" class="headerlink" title="spark.shuffle.io.maxRetries"></a><code>spark.shuffle.io.maxRetries</code></h3><ul><li><p>默认值：3</p></li><li><p>参数说明：shuffle read task 从 shuffle write task 所在节点拉取属于自己的数据时，如果因为网络异常导致拉取失败，是会自动进行重试的。该参数就代表了可以重试的最大次数。如果在指定次数之内拉取还是没有成功，就可能会导致作业执行失败。</p></li><li><p>调优建议：对于那些包含了特别耗时的 shuffle 操作的作业，建议增加重试最大次数（比如 60 次），以避免由于 JVM 的 FULL GC 或者网络不稳定等因素导致的数据拉取失败。在实践中发现，对于针对超大数据量（数十亿~上百亿）的 shuffle 过程，调节该参数可以大幅度提升稳定性。</p></li></ul><h3 id="spark-shuffle-io-retryWait"><a href="#spark-shuffle-io-retryWait" class="headerlink" title="spark.shuffle.io.retryWait"></a><code>spark.shuffle.io.retryWait</code></h3><ul><li><p>默认值：5s</p></li><li><p>参数说明：shuffle read task 从 shuffle write task 所在节点拉取属于自己的数据时，如果因为网络异常导致拉取失败，是会自动进行重试的，该参数代表了每次重试拉取数据的等待间隔，默认是 5s。</p></li><li><p>调优建议：建议加大间隔时长（比如 60s），以增加 shuffle 操作的稳定性。</p></li></ul><h3 id="spark-shuffle-memoryFraction"><a href="#spark-shuffle-memoryFraction" class="headerlink" title="spark.shuffle.memoryFraction"></a><code>spark.shuffle.memoryFraction</code></h3><ul><li><p>默认值：0.2</p></li><li><p>参数说明：该参数代表了 Executor 内存中，分配给 shuffle read task 进行聚合操作的内存比例，默认是 20%。</p></li><li><p>调优建议：在资源参数调优中讲解过这个参数。如果内存充足，而且很少使用持久化操作，建议调高这个比例，给 shuffle read 的聚合操作更多内存，以避免由于内存不足导致聚合过程中频繁读写磁盘。在实践中发现，合理调节该参数可以将性能提升 10% 左右。</p></li></ul><h3 id="spark-shuffle-manager"><a href="#spark-shuffle-manager" class="headerlink" title="spark.shuffle.manager"></a><code>spark.shuffle.manager</code></h3><ul><li><p>默认值：sort</p></li><li><p>参数说明：该参数用于设置 ShuffleManager 的类型。Spark 1.5 以后，有三个可选项：<code>hash</code>、<code>sort</code> 和 <code>tungsten-sort</code>。HashShuffleManager 是 Spark 1.2 以前的默认选项，但是 Spark 1.2 以及之后的版本默认都是 SortShuffleManager 了。<code>tungsten-sort</code> 与 <code>sort</code> 类似，但是使用了 tungsten 计划中的堆外内存管理机制，内存使用效率更高。</p></li><li><p>调优建议：由于 SortShuffleManager 默认会对数据进行排序，因此如果你的业务逻辑中需要该排序机制的话，则使用默认的 SortShuffleManager 就可以；而如果你的业务逻辑不需要对数据进行排序，那么建议参考后面的几个参数调优，通过 bypass 机制或优化的 HashShuffleManager 来避免排序操作，同时提供较好的磁盘读写性能。这里要注意的是，<code>tungsten-sort</code> 要慎用，因为之前发现了一些相应的 bug。</p></li></ul><h3 id="spark-shuffle-sort-bypassMergeThreshold"><a href="#spark-shuffle-sort-bypassMergeThreshold" class="headerlink" title="spark.shuffle.sort.bypassMergeThreshold"></a><code>spark.shuffle.sort.bypassMergeThreshold</code></h3><ul><li><p>默认值：200</p></li><li><p>参数说明：当 ShuffleManager 为 SortShuffleManager 时，如果 shuffle read task 的数量小于这个阈值（默认是 200），则 shuffle write 过程中不会进行排序操作，而是直接按照未经优化的 HashShuffleManager 的方式去写数据，但是最后会将每个 task 产生的所有临时磁盘文件都合并成一个文件，并会创建单独的索引文件。</p></li><li><p>调优建议：当你使用 SortShuffleManager 时，如果的确不需要排序操作，那么建议将这个参数调大一些，大于 shuffle read task 的数量。那么此时就会自动启用 bypass 机制，map-side 就不会进行排序了，减少了排序的性能开销。但是这种方式下，依然会产生大量的磁盘文件，因此 shuffle write 性能有待提高。</p></li></ul><h3 id="spark-shuffle-consolidateFiles"><a href="#spark-shuffle-consolidateFiles" class="headerlink" title="spark.shuffle.consolidateFiles"></a><code>spark.shuffle.consolidateFiles</code></h3><ul><li><p>默认值：false</p></li><li><p>参数说明：如果使用 HashShuffleManager，该参数有效。如果设置为 true，那么就会开启 consolidate 机制，会大幅度合并 shuffle write 的输出文件，对于 shuffle read task 数量特别多的情况下，这种方法可以极大地减少磁盘 IO 开销，提升性能。</p></li><li><p>调优建议：如果的确不需要 SortShuffleManager 的排序机制，那么除了使用 bypass 机制，还可以尝试将 <code>spark.shuffle.manager</code> 参数手动指定为 hash，使用 HashShuffleManager，同时开启 consolidate 机制。在实践中尝试过，发现其性能比开启了 bypass 机制的 SortShuffleManager 要高出 10%~30%。</p></li></ul><h3 id="Shuffle-配置调优总结："><a href="#Shuffle-配置调优总结：" class="headerlink" title="Shuffle 配置调优总结："></a>Shuffle 配置调优总结：</h3><ul><li><code>spark.shuffle.file.buffer</code>：主要是设置的 Shuffle 过程中写文件的缓冲，默认 32k，如果内存足够，可以适当调大，来减少写入磁盘的数量。</li><li><code>spark.reducer.maxSizeInFight</code>：主要是设置 Shuffle 过程中读文件的缓冲区，一次能够读取多少数据，如果内存足够，可以适当扩大，减少整个网络传输次数。</li><li><code>spark.shuffle.io.maxRetries</code>：主要是设置网络连接失败时，重试次数，适当调大能够增加稳定性。</li><li><code>spark.shuffle.io.retryWait</code>：主要设置每次重试之间的间隔时间，可以适当调大，增加程序稳定性。</li><li><code>spark.shuffle.memoryFraction</code>：Shuffle 过程中的内存占用，如果程序中较多使用了 Shuffle 操作，那么可以适当调大该区域。</li><li><code>spark.shuffle.manager</code>：hash 和 sort 方式，sort 是默认，hash 在 reduce 数量 比较少的时候，效率会很高。</li><li><code>spark.shuffle.sort. bypassMergeThreshold</code>：设置的是 Sort 方式中，启用 Hash 输出方式的临界值，如果你的程序数据不需要排序，而且 reduce 数量比较少，那推荐可以适当增大临界值。</li><li><code>spark. shuffle.consolidateFiles</code>：如果你使用 Hash shuffle 方式，推荐打开该配置，实现更少的文件输出。</li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Spark/">Spark</category>
      
      
      <comments>https://blog.eurkon.com/post/cccf27af.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Hexo 博客访问日历图</title>
      <link>https://blog.eurkon.com/post/1a169b5a.html</link>
      <guid>https://blog.eurkon.com/post/1a169b5a.html</guid>
      <pubDate>Mon, 17 May 2021 04:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文教程主要针对 Hexo 博客，参考冰老师的 &lt;a href=&quot;https://zfe.space/post/hexo-githubcal</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文教程主要针对 Hexo 博客，参考冰老师的 <a href="https://zfe.space/post/hexo-githubcalendar.html">hexo-githubcalendar 插件</a>，对博客站点的每日的访问进行统计，绘制出类似与 GitHub 贡献日历的博客访问日历。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/visit_calendar/demo.png" alt="博客访问日历"></p><blockquote><ul><li>本文数据来源均为百度统计，请确保博客站点已加入百度统计，以 butterfly 主题为例，可参照 <a href="https://butterfly.js.org/posts/ceeb73f/#%E5%88%86%E6%9E%90%E7%B5%B1%E8%A8%88">Butterfly 安装文档(四) 主题配置-2</a> 的分析统计段落实现。</li><li>本地主机访问（localhost）也会记录到百度统计，推荐在 <strong>【百度统计】--【管理】--【统计规则设置】--【过滤规则设置】--【受访域名统计规则】--【勾选排除 localhost（本地主机）】</strong> 排除本地主机访问（貌似在勾选后生效，但是以前的访问记录仍会统计）。</li><li><del>本教程将会泄漏属于百度统计的站点 ID 和百度统计 AccessToken，请先前往 <a href="https://tongji.baidu.com/api/manual/">百度统计用户手册</a> 了解，介意者请谨慎部署。</del></li><li><code>2022-03-29</code> 使用 <a href="https://console.leancloud.app/apps">LeanCloud</a> 存储百度统计的 <code>AccessToken</code> 和 <code>RefreshToken</code>，并使用 Github Action 实现每周更新。</li></ul></blockquote><h2 id="博客访问统计"><a href="#博客访问统计" class="headerlink" title="博客访问统计"></a>博客访问统计</h2><h3 id="获取网站统计数据"><a href="#获取网站统计数据" class="headerlink" title="获取网站统计数据"></a>获取网站统计数据</h3><ol><li><p>已经添加百度统计分析的小伙伴可以登录<a href="https://tongji.baidu.com/web/welcome/login">百度统计</a>的官网网站，此处博主使用的是百度账号登录。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/login.png" alt="百度账号登录"></p></li><li><p>登录进入首页后可以点击<strong>基础报告</strong>查看网站访问统计数据（若无绑定网站，需要先在 <strong>管理</strong>页面--<strong>账户管理</strong>--<strong>网站列表</strong> 添加博客网址）。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/index.png" alt="首页"></p></li><li><p>此时我们想要获取这些数据可以调用<strong>百度统计 API</strong>，点击<strong>管理</strong>，进入管理页面后在左边侧边栏找到<strong>数据导出服务</strong>。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/data_export.png" alt="数据导出服务"></p></li><li><p>登录<a href="http://developer.baidu.com/console#app/project">百度开发者中心控制台</a>，创建工程，应用名称任意。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/project.png" alt="创建工程"></p></li><li><p>点击刚刚创建的工程，记录下 API Key，Secret Key。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/project_info.png" alt="基本信息"></p></li><li><p>填写下面链接参数后打开链接获取授权码，具体步骤可以参考 <a href="https://tongji.baidu.com/api/manual/">Tongji API 用户手册</a>。</p><p> <code>http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&amp;client_id=&#123;CLIENT_ID&#125;&amp;redirect_uri=&#123;REDIRECT_URI&#125;&amp;scope=basic&amp;display=popup</code></p><ul><li>API Key：{CLIENT_ID}</li><li><p>回调 URI：{REDIRECT_URI}，可以填写 <code>oob</code>。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/auth_code.png" alt="授权码"></p></li></ul></li><li><p>复制第 6 步获取的授权码，填写下面链接参数后打开链接获取 <code>ACCESS_TOKEN</code> ：。</p><p> <code>http://openapi.baidu.com/oauth/2.0/token?grant_type=authorization_code&amp;code=&#123;CODE&#125;&amp;client_id=&#123;CLIENT_ID&#125;&amp;client_secret=&#123;CLIENT_SECRET&#125;&amp;redirect_uri=&#123;REDIRECT_URI&#125;</code></p><ul><li>Auth Code：{CODE}，第 6 步获取的授权码。</li><li>API Key：{CLIENT_ID}</li><li>Secret Key：{CLIENT_SECRET}</li><li><p>回调 URI：{REDIRECT_URI}，可以填写 <code>oob</code>。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/access_token.png" alt="ACCESS_TOKEN"></p></li></ul><p><strong>注意：</strong>从上述步骤得到的数据中包含 <code>ACCESS_TOKEN</code> 和 <code>REFRESH_TOKEN</code> 两个值，其中 <code>ACCESS_TOKEN</code> 的有效期为一个月，<code>REFRESH_TOKEN</code> 的有效期为十年。<code>REFRESH_TOKEN</code> 的作用就是刷新获取新的 <code>ACCESS_TOKEN</code> 和 <code>REFRESH_TOKEN</code> ， 如此反复操作来实现 <code>ACCESS_TOKEN</code> 有效期永久的机制。</p><p>一旦 <code>ACCESS_TOKEN</code> 过期，可根据以下请求更换新的 <code>ACCESS_TOKEN</code> 和 <code>REFRESH_TOKEN</code>：</p><p><code>http://openapi.baidu.com/oauth/2.0/token?grant_type=refresh_token&amp;refresh_token=&#123;REFRESH_TOKEN&#125;&amp;client_id=&#123;CLIENT_ID&#125;&amp;client_secret=&#123;CLIENT_SECRET&#125;</code></p><ul><li>API Key：{CLIENT_ID}</li><li>Secret Key：{CLIENT_SECRET}</li><li>Refresh Token：{REFRESH_TOKEN}</li></ul></li><li><p>调用百度统计 API</p><p> 第 7 步获取的 <code>ACCESS_TOKEN</code> 是所调用 API 的用户级参数，结合各 API 的应用级参数即可正常调用 API 获取数据，填写下面链接参数后打开链接获取网站 ID：</p><p> <code>https://openapi.baidu.com/rest/2.0/tongji/config/getSiteList?access_token=&#123;ACCESS_TOKEN&#125;</code></p><ul><li><p>Access Token：{ACCESS_TOKEN}</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/site_id.png" alt="网址 ID"></p></li></ul><p>也可以在 <a href="https://tongji.baidu.com/api/debug/#">Tongji API 调试工具</a> 输入 <code>ACCESS_TOKEN</code> 获取网址 ID：</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/site_id_debug.png" alt="API 调试工具"></p></li><li><p>在 <a href="https://tongji.baidu.com/api/debug/#">Tongji API 调试工具</a> 选择需要获取的报告数据，填写 <code>ACCESS_TOKEN</code> 、必填参数、选填参数获取百度统计数据。</p></li></ol><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/site_data.png" alt="获取百度统计数据"></p><h3 id="网站统计代码"><a href="#网站统计代码" class="headerlink" title="网站统计代码"></a>网站统计代码</h3><p>以 butterfly 主题为例，可以在 <code>[Blogroot]\js\</code> 目录下新建 <code>visit_calendar.js</code> 文件，然后添加以下内容：</p><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> visit_calendar = <span class="function">(<span class="params">site_id, access_token, metrics, visit_color</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> date = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">  <span class="keyword">var</span> end_date = <span class="string">&#x27;&#x27;</span> + date.getFullYear() + (date.getMonth() &gt; <span class="number">8</span> ? (date.getMonth() + <span class="number">1</span>) : (<span class="string">&quot;0&quot;</span> + (date.getMonth() + <span class="number">1</span>))) + (date.getDate() &gt; <span class="number">9</span> ? date.getDate() : (<span class="string">&quot;0&quot;</span> + date.getDate()));</span><br><span class="line">  date.setFullYear(date.getFullYear() - <span class="number">1</span>);</span><br><span class="line">  date.setTime(date.getTime() - <span class="number">24</span> * <span class="number">3600</span> * <span class="number">1000</span> * date.getDay());</span><br><span class="line">  <span class="keyword">var</span> start_date = <span class="string">&#x27;&#x27;</span> + date.getFullYear() + (date.getMonth() &gt; <span class="number">8</span> ? (date.getMonth() + <span class="number">1</span>) : (<span class="string">&quot;0&quot;</span> + (date.getMonth() + <span class="number">1</span>))) + (date.getDate() &gt; <span class="number">9</span> ? date.getDate() : (<span class="string">&quot;0&quot;</span> + date.getDate()));</span><br><span class="line">  <span class="keyword">var</span> metrics_name = (metrics === <span class="string">&#x27;pv_count&#x27;</span> ? <span class="string">&#x27;次&#x27;</span> : (metrics === <span class="string">&#x27;visitor_count&#x27;</span> ? <span class="string">&#x27;人&#x27;</span> : <span class="string">&#x27;&#x27;</span>));</span><br><span class="line">  <span class="keyword">var</span> visit_apiurl = <span class="string">&#x27;https://baidu-tongji-api.vercel.app/api?site_id=&#x27;</span> + site_id + <span class="string">&#x27;&amp;access_token=&#x27;</span> + access_token + <span class="string">&#x27;&amp;method=overview/getTimeTrendRpt&#x27;</span> + <span class="string">&#x27;&amp;metrics=&#x27;</span> + metrics + <span class="string">&#x27;&amp;start_date=&#x27;</span> + start_date + <span class="string">&#x27;&amp;end_date=&#x27;</span> + end_date;</span><br><span class="line">  <span class="keyword">var</span> visit_fixed = <span class="string">&#x27;fixed&#x27;</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_px = <span class="string">&#x27;px&#x27;</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_month = [<span class="string">&#x27;一月&#x27;</span>, <span class="string">&#x27;二月&#x27;</span>, <span class="string">&#x27;三月&#x27;</span>, <span class="string">&#x27;四月&#x27;</span>, <span class="string">&#x27;五月&#x27;</span>, <span class="string">&#x27;六月&#x27;</span>, <span class="string">&#x27;七月&#x27;</span>, <span class="string">&#x27;八月&#x27;</span>, <span class="string">&#x27;九月&#x27;</span>, <span class="string">&#x27;十月&#x27;</span>, <span class="string">&#x27;十一月&#x27;</span>, <span class="string">&#x27;十二月&#x27;</span>];</span><br><span class="line">  <span class="keyword">var</span> visit_monthchange = [];</span><br><span class="line">  <span class="keyword">var</span> visit_oneyearbeforeday = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_thisday = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_amonthago = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_aweekago = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_weekdatacore = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_datacore = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_total = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_datadate = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_visit_data = [];</span><br><span class="line">  <span class="keyword">var</span> visit_positionplusdata = [];</span><br><span class="line">  <span class="keyword">var</span> visit_firstweek = [];</span><br><span class="line">  <span class="keyword">var</span> visit_lastweek = [];</span><br><span class="line">  <span class="keyword">var</span> visit_beforeweek = [];</span><br><span class="line">  <span class="keyword">var</span> visit_thisweekdatacore = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_mounthbeforeday = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_mounthfirstindex = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_crispedges = <span class="string">&#x27;crispedges&#x27;</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_thisdayindex = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_amonthagoindex = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">var</span> visit_amonthagoweek = [];</span><br><span class="line">  <span class="keyword">var</span> visit_firstdate = [];</span><br><span class="line">  <span class="keyword">var</span> visit_first2date = [];</span><br><span class="line">  <span class="keyword">var</span> visit_montharrbefore = [];</span><br><span class="line">  <span class="keyword">var</span> visit_monthindex = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">var</span> retinaCanvas = <span class="function">(<span class="params">canvas, context, ratio</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (ratio &gt; <span class="number">1</span>) &#123;</span><br><span class="line">      <span class="keyword">var</span> canvasWidth = canvas.width;</span><br><span class="line">      <span class="keyword">var</span> canvasHeight = canvas.height;</span><br><span class="line">      canvas.width = canvasWidth * ratio;</span><br><span class="line">      canvas.height = canvasHeight * ratio;</span><br><span class="line">      canvas.style.width = <span class="string">&#x27;100%&#x27;</span>;</span><br><span class="line">      canvas.style.height = canvasHeight + <span class="string">&#x27;px&#x27;</span>;</span><br><span class="line">      context.scale(ratio, ratio);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">responsiveChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> ratio = <span class="built_in">window</span>.devicePixelRatio || <span class="number">1</span></span><br><span class="line">    <span class="keyword">var</span> visit_tooltip_container = <span class="built_in">document</span>.getElementById(<span class="string">&#x27;visit_tooltip_container&#x27;</span>);</span><br><span class="line">    <span class="keyword">var</span> visit_x = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="keyword">var</span> visit_y = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="keyword">var</span> visit_span1 = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="keyword">var</span> visit_span2 = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&quot;visitcanvas&quot;</span>)) &#123;</span><br><span class="line">      <span class="keyword">var</span> c = <span class="built_in">document</span>.getElementById(<span class="string">&quot;visitcanvas&quot;</span>);</span><br><span class="line">      c.style.width = <span class="string">&#x27;100%&#x27;</span>;</span><br><span class="line">      c.style.height = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">      <span class="keyword">var</span> cmessage = <span class="built_in">document</span>.getElementById(<span class="string">&quot;visitmessage&quot;</span>);</span><br><span class="line">      <span class="keyword">var</span> ctx = c.getContext(<span class="string">&quot;2d&quot;</span>);</span><br><span class="line">      width = c.width = <span class="built_in">document</span>.getElementById(<span class="string">&quot;visitcalendarcanvasbox&quot;</span>).offsetWidth;</span><br><span class="line">      height = c.height = <span class="number">9</span> * <span class="number">0.96</span> * c.width / visit_data.length;</span><br><span class="line">      retinaCanvas(c, ctx, ratio)</span><br><span class="line">      <span class="keyword">var</span> linemaxwitdh = height / <span class="number">9</span>;</span><br><span class="line">      <span class="keyword">var</span> lineminwitdh = <span class="number">0.8</span> * linemaxwitdh;</span><br><span class="line">      <span class="keyword">var</span> setposition = &#123; <span class="attr">x</span>: <span class="number">0.02</span> * width, <span class="attr">y</span>: <span class="number">0.025</span> * width &#125;;</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">var</span> week <span class="keyword">in</span> visit_data) &#123;</span><br><span class="line">        weekdata = visit_data[week];</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> day <span class="keyword">in</span> weekdata) &#123;</span><br><span class="line">          <span class="keyword">var</span> dataitem = &#123; <span class="attr">date</span>: <span class="string">&quot;&quot;</span>, <span class="attr">count</span>: <span class="string">&quot;&quot;</span>, <span class="attr">x</span>: <span class="number">0</span>, <span class="attr">y</span>: <span class="number">0</span> &#125;;</span><br><span class="line">          visit_positionplusdata.push(dataitem);</span><br><span class="line">          ctx.fillStyle = visit_thiscolor(visit_color, weekdata[day].count);</span><br><span class="line">          setposition.y = <span class="built_in">Math</span>.round(setposition.y * <span class="number">100</span>) / <span class="number">100</span>;</span><br><span class="line">          dataitem.date = weekdata[day].date;</span><br><span class="line">          dataitem.count = weekdata[day].count;</span><br><span class="line">          dataitem.x = setposition.x;</span><br><span class="line">          dataitem.y = setposition.y;</span><br><span class="line">          ctx.fillRect(setposition.x, setposition.y, lineminwitdh, lineminwitdh);</span><br><span class="line">          setposition.y = setposition.y + linemaxwitdh</span><br><span class="line">        &#125;</span><br><span class="line">        setposition.y = <span class="number">0.025</span> * width;</span><br><span class="line">        setposition.x = setposition.x + linemaxwitdh</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">document</span>.body.clientWidth &gt; <span class="number">700</span>) &#123;</span><br><span class="line">        ctx.font = <span class="string">&quot;600  Arial&quot;</span>;</span><br><span class="line">        ctx.fillStyle = <span class="string">&#x27;#aaa&#x27;</span>;</span><br><span class="line">        ctx.fillText(<span class="string">&quot;日&quot;</span>, <span class="number">0</span>, <span class="number">1.9</span> * linemaxwitdh);</span><br><span class="line">        ctx.fillText(<span class="string">&quot;二&quot;</span>, <span class="number">0</span>, <span class="number">3.9</span> * linemaxwitdh);</span><br><span class="line">        ctx.fillText(<span class="string">&quot;四&quot;</span>, <span class="number">0</span>, <span class="number">5.9</span> * linemaxwitdh);</span><br><span class="line">        ctx.fillText(<span class="string">&quot;六&quot;</span>, <span class="number">0</span>, <span class="number">7.9</span> * linemaxwitdh);</span><br><span class="line">        <span class="keyword">var</span> monthindexlist = width / <span class="number">24</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> index <span class="keyword">in</span> visit_monthchange) &#123;</span><br><span class="line">          ctx.fillText(visit_monthchange[index], monthindexlist, <span class="number">0.7</span> * linemaxwitdh);</span><br><span class="line">          monthindexlist = monthindexlist + width / <span class="number">12</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      c.onmousemove = <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">document</span>.querySelector(<span class="string">&#x27;.visitmessage&#x27;</span>)) &#123;</span><br><span class="line">          visit_tooltip_container.innerHTML = <span class="string">&quot;&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">        getMousePos(c, event)</span><br><span class="line">      &#125;;</span><br><span class="line">      visit_tooltip_container.onmousemove = <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">document</span>.querySelector(<span class="string">&#x27;.visitmessage&#x27;</span>)) &#123;</span><br><span class="line">          visit_tooltip_container.innerHTML = <span class="string">&quot;&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;;</span><br><span class="line"></span><br><span class="line">      <span class="function"><span class="keyword">function</span> <span class="title">getMousePos</span> (<span class="params">canvas, event</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> rect = canvas.getBoundingClientRect();</span><br><span class="line">        <span class="keyword">var</span> x = event.clientX - rect.left * (canvas.width / rect.width);</span><br><span class="line">        <span class="keyword">var</span> y = event.clientY - rect.top * (canvas.height / rect.height);</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> item <span class="keyword">of</span> visit_positionplusdata) &#123;</span><br><span class="line">          <span class="keyword">var</span> lenthx = x - item.x;</span><br><span class="line">          <span class="keyword">var</span> lenthy = y - item.y;</span><br><span class="line">          <span class="keyword">if</span> (<span class="number">0</span> &lt; lenthx &amp;&amp; lenthx &lt; lineminwitdh) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="number">0</span> &lt; lenthy &amp;&amp; lenthy &lt; lineminwitdh) &#123;</span><br><span class="line">              visit_span1 = item.date;</span><br><span class="line">              visit_span2 = item.count;</span><br><span class="line">              visit_x = event.clientX - <span class="number">100</span>;</span><br><span class="line">              visit_y = event.clientY - <span class="number">60</span>;</span><br><span class="line">              html = tooltip_html(visit_x, visit_y, visit_span1, visit_span2);</span><br><span class="line">              append_div_visitcalendar(visit_tooltip_container, html)</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">addlastmonth</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (visit_thisdayindex === <span class="number">0</span>) &#123;</span><br><span class="line">      thisweekcore(<span class="number">52</span>);</span><br><span class="line">      thisweekcore(<span class="number">51</span>);</span><br><span class="line">      thisweekcore(<span class="number">50</span>);</span><br><span class="line">      thisweekcore(<span class="number">49</span>);</span><br><span class="line">      thisweekcore(<span class="number">48</span>);</span><br><span class="line">      visit_thisweekdatacore += visit_firstdate[<span class="number">6</span>].count;</span><br><span class="line">      visit_amonthago = visit_firstdate[<span class="number">6</span>].date</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      thisweekcore(<span class="number">52</span>);</span><br><span class="line">      thisweekcore(<span class="number">51</span>);</span><br><span class="line">      thisweekcore(<span class="number">50</span>);</span><br><span class="line">      thisweekcore(<span class="number">49</span>);</span><br><span class="line">      thisweek2core();</span><br><span class="line">      visit_amonthago = visit_first2date[visit_thisdayindex - <span class="number">1</span>].date</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">thisweek2core</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = visit_thisdayindex - <span class="number">1</span>; i &lt; visit_first2date.length; i++) &#123;</span><br><span class="line">      visit_thisweekdatacore += visit_first2date[i].count</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">thisweekcore</span> (<span class="params">index</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> item <span class="keyword">of</span> visit_data[index]) &#123;</span><br><span class="line">      visit_thisweekdatacore += item.count</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">addlastweek</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> item <span class="keyword">of</span> visit_lastweek) &#123;</span><br><span class="line">      visit_weekdatacore += item.count</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">addbeforeweek</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = visit_thisdayindex; i &lt; visit_beforeweek.length; i++) &#123;</span><br><span class="line">      visit_weekdatacore += visit_beforeweek[i].count</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">addweek</span> (<span class="params">data</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (visit_thisdayindex === <span class="number">6</span>) &#123;</span><br><span class="line">      visit_aweekago = visit_lastweek[<span class="number">0</span>].date;</span><br><span class="line">      addlastweek()</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      lastweek = data.contributions[<span class="number">51</span>];</span><br><span class="line">      visit_aweekago = lastweek[visit_thisdayindex + <span class="number">1</span>].date;</span><br><span class="line">      addlastweek();</span><br><span class="line">      addbeforeweek()</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">spArr</span> (<span class="params">arr, num</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> newArr = []</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length;) &#123;</span><br><span class="line">      newArr.push(arr.slice(i, i += num));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> newArr</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  fetch(visit_apiurl).then(<span class="function"><span class="params">data</span> =&gt;</span> data.json()).then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> date_arr = res.result.items[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">var</span> value_arr = res.result.items[<span class="number">1</span>];</span><br><span class="line">    <span class="keyword">var</span> total = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">var</span> contributions = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; date_arr.length; i++) &#123;</span><br><span class="line">      <span class="keyword">if</span> (value_arr[i][<span class="number">0</span>] !== <span class="string">&#x27;--&#x27;</span>) &#123;</span><br><span class="line">        contributions.push(&#123; <span class="attr">date</span>: date_arr[i][<span class="number">0</span>].replace(<span class="regexp">/\//g</span>, <span class="string">&#x27;-&#x27;</span>), <span class="attr">count</span>: value_arr[i][<span class="number">0</span>] &#125;);</span><br><span class="line">        total += value_arr[i][<span class="number">0</span>];</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        contributions.push(&#123; <span class="attr">date</span>: date_arr[i][<span class="number">0</span>].replace(<span class="regexp">/\//g</span>, <span class="string">&#x27;-&#x27;</span>), <span class="attr">count</span>: <span class="number">0</span> &#125;);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">var</span> data = &#123;</span><br><span class="line">      total: total,</span><br><span class="line">      contributions: spArr(contributions, <span class="number">7</span>)</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    visit_data = data.contributions;</span><br><span class="line">    visit_total = data.total;</span><br><span class="line">    visit_first2date = visit_data[<span class="number">48</span>];</span><br><span class="line">    visit_firstdate = visit_data[<span class="number">47</span>];</span><br><span class="line">    visit_firstweek = data.contributions[<span class="number">0</span>];</span><br><span class="line">    visit_lastweek = data.contributions[<span class="number">52</span>];</span><br><span class="line">    visit_beforeweek = data.contributions[<span class="number">51</span>];</span><br><span class="line">    visit_thisdayindex = visit_lastweek.length - <span class="number">1</span>;</span><br><span class="line">    visit_thisday = visit_lastweek[visit_thisdayindex].date;</span><br><span class="line">    visit_oneyearbeforeday = visit_firstweek[<span class="number">0</span>].date;</span><br><span class="line">    visit_monthindex = visit_thisday.substring(<span class="number">5</span>, <span class="number">7</span>) * <span class="number">1</span>;</span><br><span class="line">    visit_montharrbefore = visit_month.splice(visit_monthindex, <span class="number">12</span> - visit_monthindex);</span><br><span class="line">    visit_monthchange = visit_montharrbefore.concat(visit_month);</span><br><span class="line">    addweek(data);</span><br><span class="line">    addlastmonth();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> html = visit_main_box(visit_monthchange, visit_data, visit_color, visit_total, visit_thisweekdatacore, visit_weekdatacore, visit_oneyearbeforeday, visit_thisday, visit_aweekago, visit_amonthago);</span><br><span class="line">    append_div_visitcalendar(<span class="built_in">document</span>.getElementById(<span class="string">&#x27;visit_container&#x27;</span>), html);</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;visit_loading&#x27;</span>)) &#123;</span><br><span class="line">      <span class="built_in">document</span>.getElementById(<span class="string">&#x27;visit_loading&#x27;</span>).remove()</span><br><span class="line">    &#125;;</span><br><span class="line">    responsiveChart()</span><br><span class="line">  &#125;).catch(<span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(error)</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="built_in">window</span>.onresize = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    responsiveChart()</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="built_in">window</span>.onscroll = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">document</span>.querySelector(<span class="string">&#x27;.visitmessage&#x27;</span>)) &#123;</span><br><span class="line">      visit_tooltip_container.innerHTML = <span class="string">&quot;&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">var</span> visit_thiscolor = <span class="function">(<span class="params">color, x</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (metrics === <span class="string">&#x27;pv_count&#x27;</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (x === <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> i = <span class="built_in">parseInt</span>(x / <span class="number">20</span>);</span><br><span class="line">        <span class="keyword">return</span> color[<span class="number">0</span>]</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (x &lt; <span class="number">20</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> color[<span class="number">1</span>]</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (x &lt; <span class="number">200</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> i = <span class="built_in">parseInt</span>(x / <span class="number">20</span>);</span><br><span class="line">        <span class="keyword">return</span> color[i]</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> color[<span class="number">9</span>]</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (x === <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> i = <span class="built_in">parseInt</span>(x / <span class="number">2</span>);</span><br><span class="line">        <span class="keyword">return</span> color[<span class="number">0</span>]</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (x &lt; <span class="number">2</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> color[<span class="number">1</span>]</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (x &lt; <span class="number">20</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> i = <span class="built_in">parseInt</span>(x / <span class="number">2</span>);</span><br><span class="line">        <span class="keyword">return</span> color[i]</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> color[<span class="number">9</span>]</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">var</span> tooltip_html = <span class="function">(<span class="params">x, y, span1, span2</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> html = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    html += <span class="string">&#x27;&lt;div class=&quot;visitmessage&quot; style=&quot;top:&#x27;</span> + y + <span class="string">&#x27;px;left:&#x27;</span> + x + <span class="string">&#x27;px;position: fixed;z-index:9999&quot;&gt;&lt;div class=&quot;angle-wrapper&quot; style=&quot;display:block;&quot;&gt;&lt;span&gt;&#x27;</span> + span1 + <span class="string">&#x27;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&#x27;</span> + span2 + <span class="string">&#x27; &#x27;</span> + metrics_name + <span class="string">&#x27;访问&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&#x27;</span>;</span><br><span class="line">    <span class="keyword">return</span> html</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">var</span> visit_canvas_box = <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> html = <span class="string">&#x27;&lt;div id=&quot;visitcalendarcanvasbox&quot;&gt; &lt;canvas id=&quot;visitcanvas&quot; style=&quot;animation: none;&quot;&gt;&lt;/canvas&gt;&lt;/div&gt;&#x27;</span>;</span><br><span class="line">    <span class="keyword">return</span> html</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">var</span> visit_info_box = <span class="function">(<span class="params">color</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> html = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    html += <span class="string">&#x27;&lt;div id=&quot;visit_tooltip_container&quot;&gt;&lt;/div&gt;&lt;div class=&quot;contrib-footer clearfix mt-1 mx-3 px-3 pb-1&quot;&gt;&lt;div class=&quot;float-left text-gray&quot;&gt;数据来源 &lt;a href=&quot;https://tongji.baidu.com/&quot; target=&quot;blank&quot;&gt;百度统计&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;contrib-legend text-gray&quot;&gt;Less &lt;ul class=&quot;legend&quot;&gt;&lt;li style=&quot;background-color:&#x27;</span> + color[<span class="number">0</span>] + <span class="string">&#x27;&quot;&gt;&lt;/li&gt;&lt;li style=&quot;background-color:&#x27;</span> + color[<span class="number">2</span>] + <span class="string">&#x27;&quot;&gt;&lt;/li&gt;&lt;li style=&quot;background-color:&#x27;</span> + color[<span class="number">4</span>] + <span class="string">&#x27;&quot;&gt;&lt;/li&gt;&lt;li style=&quot;background-color:&#x27;</span> + color[<span class="number">6</span>] + <span class="string">&#x27;&quot;&gt;&lt;/li&gt;&lt;li style=&quot;background-color:&#x27;</span> + color[<span class="number">8</span>] + <span class="string">&#x27;&quot;&gt;&lt;/li&gt;&lt;/ul&gt;More &lt;/div&gt;&lt;/div&gt;&#x27;</span>;</span><br><span class="line">    <span class="keyword">return</span> html</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">var</span> visit_main_box = <span class="function">(<span class="params">monthchange, visit_data, color, total, thisweekdatacore, weekdatacore, oneyearbeforeday, thisday, aweekago, amonthago</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> html = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="keyword">var</span> canvasbox = visit_canvas_box();</span><br><span class="line">    <span class="keyword">var</span> infobox = visit_info_box(color);</span><br><span class="line">    <span class="keyword">var</span> style = visit_main_style();</span><br><span class="line">    html += <span class="string">&#x27;&lt;div class=&quot;position-relative&quot;&gt;&lt;div&gt;&lt;span class=&quot;visit_title&quot;&gt;博客访问日历&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;border py-2 graph-before-activity-overview&quot;&gt;&lt;div class=&quot;js-visitcalendar-graph mx-md-2 mx-3 d-flex flex-column flex-items-end flex-xl-items-center overflow-hidden pt-1 is-graph-loading graph-canvas visitcalendar-graph height-full text-center&quot;&gt;&#x27;</span> + canvasbox + <span class="string">&#x27;&lt;/div&gt;&#x27;</span> + infobox + <span class="string">&#x27;&lt;/div&gt;&lt;/div&gt;&#x27;</span>;</span><br><span class="line">    html += <span class="string">&#x27;&lt;div style=&quot;display:flex;width:100%&quot;&gt;&lt;div class=&quot;contrib-column contrib-column-first table-column&quot;&gt;&lt;span class=&quot;text-muted&quot;&gt;过去一年访问&lt;/span&gt;&lt;span class=&quot;contrib-number&quot;&gt;&#x27;</span> + total + <span class="string">&#x27;&lt;/span&gt;&lt;span class=&quot;text-muted&quot;&gt;&#x27;</span> + oneyearbeforeday + <span class="string">&#x27;&amp;nbsp;-&amp;nbsp;&#x27;</span> + thisday + <span class="string">&#x27;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;contrib-column table-column&quot;&gt;&lt;span class=&quot;text-muted&quot;&gt;最近一月访问&lt;/span&gt;&lt;span class=&quot;contrib-number&quot;&gt;&#x27;</span> + thisweekdatacore + <span class="string">&#x27;&lt;/span&gt;&lt;span class=&quot;text-muted&quot;&gt;&#x27;</span> + amonthago + <span class="string">&#x27;&amp;nbsp;-&amp;nbsp;&#x27;</span> + thisday + <span class="string">&#x27;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;contrib-column table-column&quot;&gt;&lt;span class=&quot;text-muted&quot;&gt;最近一周访问&lt;/span&gt;&lt;span class=&quot;contrib-number&quot;&gt;&#x27;</span> + weekdatacore + <span class="string">&#x27;&lt;/span&gt;&lt;span class=&quot;text-muted&quot;&gt;&#x27;</span> + aweekago + <span class="string">&#x27;&amp;nbsp;-&amp;nbsp;&#x27;</span> + thisday + <span class="string">&#x27;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&#x27;</span> + style;</span><br><span class="line">    <span class="keyword">return</span> html</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">var</span> visit_main_style = <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    style = <span class="string">&#x27;&lt;style&gt;#visit_container&#123;text-align:center;margin:0 auto;width:100%;padding:10px;display:flex;display:-webkit-flex;justify-content:center;align-items:center;flex-wrap:wrap;&#125;.visit_title&#123;font-size:1rem;font-weight:550;&#125;.visitcalendar-graph text.wday,.visitcalendar-graph text.month&#123;font-size:10px;fill:#aaa;&#125;.contrib-legend&#123;text-align:right;padding:0 14px 10px 0;display:inline-block;float:right;&#125;.contrib-legend .legend&#123;display:inline-block;list-style:none;margin:0 5px;position:relative;bottom:-7px;padding:0;&#125;.contrib-legend .legend li&#123;display:inline-block;width:10px;height:10px;&#125;.text-small&#123;font-size:12px;color:#767676;&#125;.visitcalendar-graph&#123;padding:15px 0 0;text-align:center;&#125;.contrib-column&#123;text-align:center;border-left:1px solid #ddd;border-top:1px solid #ddd;&#125;.contrib-column-first&#123;border-left:0;&#125;.table-column&#123;padding:10px;display:table-cell;flex:1;vertical-align:top;&#125;.contrib-number&#123;font-weight:400;line-height:1.3em;font-size:24px;display:block;&#125;.visitcalendar img.spinner&#123;width:70px;margin-top:50px;min-height:70px;&#125;.monospace&#123;text-align:center;color:#000;font-family:monospace;&#125;.monospace a&#123;color:#1D75AB;text-decoration:none;&#125;.contrib-footer&#123;font-size:11px;padding:0 10px 12px;text-align:left;width:100%;box-sizing:border-box;height:26px;&#125;.left.text-muted&#123;float:left;margin-left:9px;color:#767676;&#125;.left.text-muted a&#123;color:#4078c0;text-decoration:none;&#125;.left.text-muted a:hover,.monospace a:hover&#123;text-decoration:underline;&#125;h2.f4.text-normal.mb-3&#123;display:none;&#125;.float-left.text-gray&#123;float:left;&#125;#user-activity-overview&#123;display:none;&#125;.day-tooltip&#123;white-space:nowrap;position:absolute;z-index:99999;padding:10px;font-size:12px;color:#959da5;text-align:center;background:rgba(0,0,0,.85);border-radius:3px;display:none;pointer-events:none;&#125;.day-tooltip strong&#123;color:#dfe2e5;&#125;.day-tooltip.is-visible&#123;display:block;&#125;.day-tooltip:after&#123;position:absolute;bottom:-10px;left:50%;width:5px;height:5px;box-sizing:border-box;margin:0 0 0 -5px;content:&quot; &quot;;border:5px solid transparent;border-top-color:rgba(0,0,0,.85)&#125;.position-relative&#123;width:100%;&#125;@media screen and (max-width:650px)&#123;.contrib-column&#123;display:none&#125;&#125;.angle-wrapper&#123;z-index:9999;display:inline;width:200px;height:40px;position:relative;padding:5px 0;background:rgba(0,0,0,0.8);border-radius:8px;text-align:center;color:white;&#125;.angle-box&#123;position:fixed;padding:10px&#125;.angle-wrapper span&#123;padding-bottom:1em;&#125;.angle-wrapper:before&#123;content:&quot;&quot;;width:0;height:0;border:10px solid transparent;border-top-color:rgba(0,0,0,0.8);position:absolute;left:47.5%;top:100%;&#125;&lt;/style&gt;&#x27;</span>;</span><br><span class="line">    <span class="keyword">return</span> style</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> append_div_visitcalendar = <span class="function">(<span class="params">parent, text</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (parent !== <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> text === <span class="string">&#x27;string&#x27;</span>) &#123;</span><br><span class="line">      <span class="keyword">var</span> temp = <span class="built_in">document</span>.createElement(<span class="string">&#x27;div&#x27;</span>);</span><br><span class="line">      temp.innerHTML = text;</span><br><span class="line">      <span class="keyword">var</span> frag = <span class="built_in">document</span>.createDocumentFragment();</span><br><span class="line">      <span class="keyword">while</span> (temp.firstChild) &#123;</span><br><span class="line">        frag.appendChild(temp.firstChild)</span><br><span class="line">      &#125;</span><br><span class="line">      parent.appendChild(frag)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      parent.appendChild(text)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> loading_visit = <span class="function">(<span class="params">color</span>) =&gt;</span> &#123;</span><br><span class="line">  loading = <span class="string">&#x27;&lt;div id=&quot;visit_loading&quot; style=&quot;width:10%;height:100%;margin:0 auto;display: block&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot;  viewBox=&quot;0 0 50 50&quot; style=&quot;enable-background:new 0 0 50 50&quot; xml:space=&quot;preserve&quot;&gt;&lt;path fill=&quot;&#x27;</span> + color + <span class="string">&#x27;&quot; d=&quot;M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z&quot; transform=&quot;rotate(275.098 25 25)&quot;&gt;&lt;animateTransform attributeType=&quot;xml&quot; attributeName=&quot;transform&quot; type=&quot;rotate&quot; from=&quot;0 25 25&quot; to=&quot;360 25 25&quot; dur=&quot;0.6s&quot; repeatCount=&quot;indefinite&quot;&gt;&lt;/animateTransform&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&#x27;</span>;</span><br><span class="line">  <span class="keyword">return</span> loading</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">(<span class="function"><span class="keyword">function</span> <span class="title">init</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> visit_container = <span class="built_in">document</span>.getElementById(<span class="string">&#x27;visit_container&#x27;</span>);</span><br><span class="line">  <span class="keyword">var</span> visit_loading = <span class="built_in">document</span>.getElementById(<span class="string">&#x27;visit_loading&#x27;</span>);</span><br><span class="line">  <span class="keyword">var</span> visit_purple = [<span class="string">&#x27;#d7dbe2&#x27;</span>, <span class="string">&#x27;#fdcdec&#x27;</span>, <span class="string">&#x27;#fc9bd9&#x27;</span>, <span class="string">&#x27;#fa6ac5&#x27;</span>, <span class="string">&#x27;#f838b2&#x27;</span>, <span class="string">&#x27;#f5089f&#x27;</span>, <span class="string">&#x27;#c4067e&#x27;</span>, <span class="string">&#x27;#92055e&#x27;</span>, <span class="string">&#x27;#540336&#x27;</span>, <span class="string">&#x27;#48022f&#x27;</span>, <span class="string">&#x27;#30021f&#x27;</span>];</span><br><span class="line">  <span class="keyword">var</span> visit_yellow = [<span class="string">&#x27;#d7dbe2&#x27;</span>, <span class="string">&#x27;#f9f4dc&#x27;</span>, <span class="string">&#x27;#f7e8aa&#x27;</span>, <span class="string">&#x27;#f7e8aa&#x27;</span>, <span class="string">&#x27;#f8df72&#x27;</span>, <span class="string">&#x27;#fcd217&#x27;</span>, <span class="string">&#x27;#fcc515&#x27;</span>, <span class="string">&#x27;#f28e16&#x27;</span>, <span class="string">&#x27;#fb8b05&#x27;</span>, <span class="string">&#x27;#d85916&#x27;</span>, <span class="string">&#x27;#f43e06&#x27;</span>];</span><br><span class="line">  <span class="keyword">var</span> visit_green = [<span class="string">&#x27;#d7dbe2&#x27;</span>, <span class="string">&#x27;#f0fff4&#x27;</span>, <span class="string">&#x27;#dcffe4&#x27;</span>, <span class="string">&#x27;#bef5cb&#x27;</span>, <span class="string">&#x27;#85e89d&#x27;</span>, <span class="string">&#x27;#34d058&#x27;</span>, <span class="string">&#x27;#28a745&#x27;</span>, <span class="string">&#x27;#22863a&#x27;</span>, <span class="string">&#x27;#176f2c&#x27;</span>, <span class="string">&#x27;#165c26&#x27;</span>, <span class="string">&#x27;#144620&#x27;</span>];</span><br><span class="line">  <span class="keyword">var</span> visit_blue = [<span class="string">&#x27;#d7dbe2&#x27;</span>, <span class="string">&#x27;#f1f8ff&#x27;</span>, <span class="string">&#x27;#dbedff&#x27;</span>, <span class="string">&#x27;#c8e1ff&#x27;</span>, <span class="string">&#x27;#79b8ff&#x27;</span>, <span class="string">&#x27;#2188ff&#x27;</span>, <span class="string">&#x27;#0366d6&#x27;</span>, <span class="string">&#x27;#005cc5&#x27;</span>, <span class="string">&#x27;#044289&#x27;</span>, <span class="string">&#x27;#032f62&#x27;</span>, <span class="string">&#x27;#05264c&#x27;</span>];</span><br><span class="line">  <span class="keyword">var</span> visit_color = visit_purple;</span><br><span class="line">  append_div_visitcalendar(visit_container, loading_visit(visit_color[<span class="number">4</span>]));</span><br><span class="line">  <span class="comment">// 统计访问次数 PV 填写 &#x27;pv_count&#x27;，统计访客数 UV 填写 &#x27;visitor_count&#x27;，二选一</span></span><br><span class="line">  visit_calendar(<span class="string">&#x27;16****&#x27;</span>, <span class="string">&#x27;121.c644d8c4*****&#x27;</span>, <span class="string">&#x27;pv_count&#x27;</span>, visit_color)</span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure></p><h2 id="项目部署（可选）"><a href="#项目部署（可选）" class="headerlink" title="项目部署（可选）"></a>项目部署（可选）</h2><p>使用 Github + LeanCloud + Vercel</p><p>整个部署均在云端完成。运行原理：</p><ol><li>数据存储在 LeanCloud 数据库，也可以自行修改源码存储在其他数据库；</li><li>通过 Github Action 更新百度统计的 <code>AccessToken</code> 和 <code>RefreshToken</code>；</li><li>Vercel 部署的 API 从 LeanCloud 获取 <code>AccessToken</code>，再执行请求，获取百度统计数据，返回给前端。</li></ol><h3 id="LeanCloud-部署"><a href="#LeanCloud-部署" class="headerlink" title="LeanCloud 部署"></a>LeanCloud 部署</h3><ol><li><p>新建结构化数据，创建 Class，填写名称为 <code>BaiduToken</code>，设置默认 ACL read 和 write 权限都为所有用户；</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/leancloud_class.png" alt="LeanCloud 新建 BaiduToken Class"></p></li><li><p>点击添加列，分别新增 <code>accessToken</code> 和 <code>refreshToken</code>；</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/leancloud_add_column.png" alt="BaiduToken Class 添加列 "></p></li><li><p>点击新增行，填写前面获取到的 <code>access_token</code> 和 <code>refresh_token</code>；</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/access_token.png" alt="BaiduToken"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/leancloud_add_row.png" alt="BaiduToken Class 添加列 "></p></li></ol><h3 id="GitHub-部署"><a href="#GitHub-部署" class="headerlink" title="GitHub 部署"></a>GitHub 部署</h3><p>由于在 js 文件中请求百度统计 API 获取数据会出现跨域请求的错误。</p><p>博主参考 Akilar 的 <a href="https://akilar.top/posts/1f9c68c9/">基于 Butterfly 主题的首页置顶 gitcalendar</a> 自建 Vercel API 部署教程，以及冰老师的 <a href="https://github.com/Zfour/python_github_calendar_api">python_github_calendar_api</a> 搭建了获取百度统计数据的 Vercel API，力求解决百度统计 API 的跨域请求问题。</p><p><a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/Eurkon/baidu-tongji-api"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://github-readme-stats.eurkon.com/api/pin/?username=Eurkon&repo=baidu-tongji-api&show_owner=true"></a></p><p>虽然 Vercel 的访问应当没有次数限制，但是不排除存在因访问次数过多而限流等限制。所以还是建议各位自建 API。使用 Vercel 部署，完全免费。且无需服务器。</p><ol><li><p>在 baidu-tongji-api 项目 <strong>【Settings】--【Security】--【Secrets】--【Actions】</strong> 中添加以下秘钥：</p><ul><li><code>APIKEY</code>： 百度统计 API Key</li><li><code>SECRETKEY</code>：百度统计 Secret Key</li><li><code>APPID</code>：LeanCloud AppID</li><li><code>APPKEY</code>：LeanCloud AppKey</li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/github_action_secret.png" alt="Github Action Secret "></p></li></ol><p><!-- 2. 添加秘钥完成后可以点击项目 **【Star】** 测试 Github Action 是否能够执行，关注 LeanCloud 是否更新 Token 成功 --></p><h3 id="Vercel-部署"><a href="#Vercel-部署" class="headerlink" title="Vercel 部署"></a>Vercel 部署</h3><p>部署 Vercel 项目后添加 LeanCloud 的环境变量。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/vercel_environment_variables.png" alt="Vercel 添加环境变量"></p><p>添加环境变量完成后，将获取到的默认域名替换 <code>visit_calendar.js</code> 文件中的 <code>visit_apiurl</code>，如：</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">- var visit_apiurl = &#x27;https://baidu-tongji-api.vercel.app/api?site_id=&#x27; + site_id + &#x27;&amp;access_token=&#x27; + access_token + &#x27;&amp;method=overview/getTimeTrendRpt&#x27; + &#x27;&amp;metrics=&#x27; + metrics + &#x27;&amp;start_date=&#x27; + start_date + &#x27;&amp;end_date=&#x27; + end_date;</span></span><br><span class="line"><span class="addition">+ var visit_apiurl = &#x27;https://域名/api?site_id=&#x27; + site_id + &#x27;&amp;access_token=&#x27; + access_token + &#x27;&amp;method=overview/getTimeTrendRpt&#x27; + &#x27;&amp;metrics=&#x27; + metrics + &#x27;&amp;start_date=&#x27; + start_date + &#x27;&amp;end_date=&#x27; + end_date;</span></span><br></pre></td></tr></table></figure></p><p>如果使用了数据库存储 <code>visit_apiurl</code>，则可以省略 <code>visit_apiurl</code> 参数。</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">- var visit_apiurl = &#x27;https://baidu-tongji-api.vercel.app/api?site_id=&#x27; + site_id + &#x27;&amp;method=overview/getTimeTrendRpt&#x27; + &#x27;&amp;metrics=&#x27; + metrics + &#x27;&amp;start_date=&#x27; + start_date + &#x27;&amp;end_date=&#x27; + end_date;</span></span><br><span class="line"><span class="addition">+ var visit_apiurl = &#x27;https://域名/api?site_id=&#x27; + site_id + &#x27;&amp;method=overview/getTimeTrendRpt&#x27; + &#x27;&amp;metrics=&#x27; + metrics + &#x27;&amp;start_date=&#x27; + start_date + &#x27;&amp;end_date=&#x27; + end_date;</span></span><br><span class="line"></span><br><span class="line">// 统计访问次数 PV 填写 &#x27;pv_count&#x27;，统计访客数 UV 填写 &#x27;visitor_count&#x27;，二选一</span><br><span class="line"><span class="deletion">- visit_calendar(&#x27;16****&#x27;, &#x27;121.c644d8c4*****&#x27;, &#x27;pv_count&#x27;, visit_color)</span></span><br><span class="line"><span class="addition">+ visit_calendar(&#x27;16****&#x27;, &#x27;&#x27;, &#x27;pv_count&#x27;, visit_color)</span></span><br></pre></td></tr></table></figure></p><h2 id="使用访问日历"><a href="#使用访问日历" class="headerlink" title="使用访问日历"></a>使用访问日历</h2><p>在需要显示访问日历的 <code>md</code> 文件中添加以下内容：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;visit_container&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">defer</span> <span class="attr">data-pjax</span> <span class="attr">src</span>=<span class="string">&quot;/js/visit_calendar.js&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br></pre></td></tr></table></figure></p><h2 id="可能遇到的问题"><a href="#可能遇到的问题" class="headerlink" title="可能遇到的问题"></a>可能遇到的问题</h2><ul><li><p>控制台报错 <code>Access to fetch at &#39;https://baidu-tongji-api.vercel.app/api?...&#39; from origin &#39;http://...&#39; has been blocked by CORS policy: No &#39;Access-Control-Allow-Origin&#39; header is present on the requested resource. If an opaque response serves your needs, set the request&#39;s mode to &#39;no-cors&#39; to fetch the resource with CORS disabled.</code></p><p><strong>解决方案：</strong></p><p>仍在努力解决跨域问题。</p></li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      
      <comments>https://blog.eurkon.com/post/1a169b5a.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Hexo 博客实时访问统计图</title>
      <link>https://blog.eurkon.com/post/61763977.html</link>
      <guid>https://blog.eurkon.com/post/61763977.html</guid>
      <pubDate>Mon, 17 May 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文教程主要针对 Hexo 博客，对博客站点的的访问地图、每月访问量、访问来源的维度绘制统计图，使用的是 &lt;a href=&quot;https://</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文教程主要针对 Hexo 博客，对博客站点的的访问地图、每月访问量、访问来源的维度绘制统计图，使用的是 <a href="https://echarts.apache.org/examples/zh/index.html">ECharts</a> 开源可视化库。具体效果可以点击本站的 <a href="/census/">统计--博客统计</a> 页面查看。</p><blockquote><ul><li>本文数据来源均为百度统计，请确保博客站点已加入百度统计，以 butterfly 主题为例，可参照 <a href="https://butterfly.js.org/posts/ceeb73f/#%E5%88%86%E6%9E%90%E7%B5%B1%E8%A8%88">Butterfly 安装文档(四) 主题配置-2</a> 的分析统计段落实现。</li><li>本地主机访问（localhost）也会记录到百度统计，推荐在 <strong>【百度统计】--【管理】--【统计规则设置】--【过滤规则设置】--【受访域名统计规则】--【勾选排除 localhost（本地主机）】</strong> 排除本地主机访问（貌似在勾选后生效，但是以前的访问记录仍会统计）。</li><li>此文是针对于文章 <a href="/post/ef1da941.html">Hexo 博客访问统计图</a> 改良版，实时调用百度统计 API 获取访问数据。</li><li><del>本教程将会泄漏属于百度统计的站点 ID 和百度统计 AccessToken，请先前往 <a href="https://tongji.baidu.com/api/manual/">百度统计用户手册</a> 了解，介意者请谨慎部署。</del></li><li><code>2021-05-14</code> 新增访客数的统计，统计指标 <code>metrics</code> 可以选择统计访问次数（PV）还是访客数（UV）。</li><li><code>2021-05-17</code> 新增主题“明暗模式”下统计图的颜色切换。</li><li><code>2022-03-29</code> 使用 <a href="https://console.leancloud.app/apps">LeanCloud</a> 存储百度统计的 <code>AccessToken</code> 和 <code>RefreshToken</code>，并使用 Github Action 实现每周更新。</li><li><code>2022-05-31</code> 更换 <code>mapChart</code> 省份地域访问次数的请求地址，<code>visit/district/a =&gt; overview/getDistrictRpt</code>（原地址获取省份不全），修改处理逻辑 <code>mapArr.push(&#123; name: mapName[i][0].name, value: mapValue[i][0] &#125;) =&gt; mapArr.push(&#123; name: mapName[i][0], value: mapValue[i][0] &#125;)</code>。</li></ul></blockquote><p>如果想绘制博客文章发布统计图的可以参考文章 <a href="/post/1213ef82.html">Hexo 博客文章统计图</a></p><h2 id="新建-census-页面"><a href="#新建-census-页面" class="headerlink" title="新建 census 页面"></a>新建 census 页面</h2><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab active">使用命令创建</button><button type="button" class="tab">手动创建</button></div><div class="tab-contents"><div class="tab-item-content active"><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo new page census</span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content"><p>在 <code>[Blogroot]\source\</code> 目录下新建 <code>census</code> 文件夹，并在新建的 <code>census</code> 文件夹下新建 <code>index.md</code> 文件，添加以下内容：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: 博客统计</span><br><span class="line">date: 2020-03-01 08:00:00</span><br><span class="line">---</span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><h2 id="引入-ECharts-js"><a href="#引入-ECharts-js" class="headerlink" title="引入 ECharts.js"></a>引入 ECharts.js</h2><blockquote><p>echarts.js 必须在渲染 echarts 实例的 JavaScript 前引入。</p></blockquote><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab active">全局引入</button><button type="button" class="tab">页面引入</button></div><div class="tab-contents"><div class="tab-item-content active"><p>以 butterfly 主题为例，可以在 <code>[Blogroot]\_config.butterfly.yml</code> 的 <code>inject</code> 配置项中引入 <code>echart.js</code> 文件。</p><p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line">  <span class="attr">head:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&lt;script</span> <span class="string">src=&quot;https://npm.elemecdn.com/echarts@4.9.0/dist/echarts.min.js&quot;&gt;&lt;/script&gt;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&lt;script</span> <span class="string">src=&quot;https://npm.elemecdn.com/echarts@4.9.0/map/js/china.js&quot;&gt;&lt;/script&gt;</span> <span class="comment"># 绘制地图需要另外添加 china.js</span></span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content"><p>可以在 <code>index.md</code> 添加以下内容：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;https://npm.elemecdn.com/echarts@4.9.0/dist/echarts.min.js&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;https://npm.elemecdn.com/echarts@4.9.0/map/js/china.js&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span> &lt;!-- 绘制地图需要另外添加 china.js --&gt;</span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><h2 id="博客访问统计"><a href="#博客访问统计" class="headerlink" title="博客访问统计"></a>博客访问统计</h2><h3 id="获取网站统计数据"><a href="#获取网站统计数据" class="headerlink" title="获取网站统计数据"></a>获取网站统计数据</h3><ol><li><p>已经添加百度统计分析的小伙伴可以登录<a href="https://tongji.baidu.com/web/welcome/login">百度统计</a>的官网网站，此处博主使用的是百度账号登录。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/login.png" alt="百度账号登录"></p></li><li><p>登录进入首页后可以点击<strong>基础报告</strong>查看网站访问统计数据（若无绑定网站，需要先在 <strong>管理</strong>页面--<strong>账户管理</strong>--<strong>网站列表</strong> 添加博客网址）。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/index.png" alt="首页"></p></li><li><p>此时我们想要获取这些数据可以调用<strong>百度统计 API</strong>，点击<strong>管理</strong>，进入管理页面后在左边侧边栏找到<strong>数据导出服务</strong>。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/data_export.png" alt="数据导出服务"></p></li><li><p>登录<a href="http://developer.baidu.com/console#app/project">百度开发者中心控制台</a>，创建工程，应用名称任意。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/project.png" alt="创建工程"></p></li><li><p>点击刚刚创建的工程，记录下 API Key，Secret Key。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/project_info.png" alt="基本信息"></p></li><li><p>填写下面链接参数后打开链接获取授权码，具体步骤可以参考 <a href="https://tongji.baidu.com/api/manual/">Tongji API 用户手册</a>。</p><p> <code>http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&amp;client_id=&#123;CLIENT_ID&#125;&amp;redirect_uri=&#123;REDIRECT_URI&#125;&amp;scope=basic&amp;display=popup</code></p><ul><li>API Key：{CLIENT_ID}</li><li><p>回调 URI：{REDIRECT_URI}，可以填写 <code>oob</code>。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/auth_code.png" alt="授权码"></p></li></ul></li><li><p>复制第 6 步获取的授权码，填写下面链接参数后打开链接获取 <code>ACCESS_TOKEN</code> ：。</p><p> <code>http://openapi.baidu.com/oauth/2.0/token?grant_type=authorization_code&amp;code=&#123;CODE&#125;&amp;client_id=&#123;CLIENT_ID&#125;&amp;client_secret=&#123;CLIENT_SECRET&#125;&amp;redirect_uri=&#123;REDIRECT_URI&#125;</code></p><ul><li>Auth Code：{CODE}，第 6 步获取的授权码。</li><li>API Key：{CLIENT_ID}</li><li>Secret Key：{CLIENT_SECRET}</li><li><p>回调 URI：{REDIRECT_URI}，可以填写 <code>oob</code>。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/access_token.png" alt="ACCESS_TOKEN"></p></li></ul><p><strong>注意：</strong>从上述步骤得到的数据中包含 <code>ACCESS_TOKEN</code> 和 <code>REFRESH_TOKEN</code> 两个值，其中 <code>ACCESS_TOKEN</code> 的有效期为一个月，<code>REFRESH_TOKEN</code> 的有效期为十年。<code>REFRESH_TOKEN</code> 的作用就是刷新获取新的 <code>ACCESS_TOKEN</code> 和 <code>REFRESH_TOKEN</code> ， 如此反复操作来实现 <code>ACCESS_TOKEN</code> 有效期永久的机制。</p><p>一旦 <code>ACCESS_TOKEN</code> 过期，可根据以下请求更换新的 <code>ACCESS_TOKEN</code> 和 <code>REFRESH_TOKEN</code>：</p><p><code>http://openapi.baidu.com/oauth/2.0/token?grant_type=refresh_token&amp;refresh_token=&#123;REFRESH_TOKEN&#125;&amp;client_id=&#123;CLIENT_ID&#125;&amp;client_secret=&#123;CLIENT_SECRET&#125;</code></p><ul><li>API Key：{CLIENT_ID}</li><li>Secret Key：{CLIENT_SECRET}</li><li>Refresh Token：{REFRESH_TOKEN}</li></ul></li><li><p>调用百度统计 API</p><p> 第 7 步获取的 <code>ACCESS_TOKEN</code> 是所调用 API 的用户级参数，结合各 API 的应用级参数即可正常调用 API 获取数据，填写下面链接参数后打开链接获取网站 ID：</p><p> <code>https://openapi.baidu.com/rest/2.0/tongji/config/getSiteList?access_token=&#123;ACCESS_TOKEN&#125;</code></p><ul><li><p>Access Token：{ACCESS_TOKEN}</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/site_id.png" alt="网址 ID"></p></li></ul><p>也可以在 <a href="https://tongji.baidu.com/api/debug/#">Tongji API 调试工具</a> 输入 <code>ACCESS_TOKEN</code> 获取网址 ID：</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/site_id_debug.png" alt="API 调试工具"></p></li><li><p>在 <a href="https://tongji.baidu.com/api/debug/#">Tongji API 调试工具</a> 选择需要获取的报告数据，填写 <code>ACCESS_TOKEN</code> 、必填参数、选填参数获取百度统计数据。</p></li></ol><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/site_data.png" alt="获取百度统计数据"></p><h3 id="网站统计代码"><a href="#网站统计代码" class="headerlink" title="网站统计代码"></a>网站统计代码</h3><p>以 butterfly 主题为例，可以在 <code>[Blogroot]\js\</code> 目录下新建 <code>census.js</code> 文件，然后添加以下内容：</p><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> start_date = <span class="string">&#x27;20210101&#x27;</span> <span class="comment">// 开始日期</span></span><br><span class="line"><span class="keyword">var</span> date = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line"><span class="keyword">var</span> end_date = <span class="string">&#x27;&#x27;</span> + date.getFullYear() + (date.getMonth() &gt; <span class="number">8</span> ? (date.getMonth() + <span class="number">1</span>) : (<span class="string">&quot;0&quot;</span> + (date.getMonth() + <span class="number">1</span>))) + (date.getDate() &gt; <span class="number">9</span> ? date.getDate() : (<span class="string">&quot;0&quot;</span> + date.getDate())); <span class="comment">// 结束日期</span></span><br><span class="line"><span class="keyword">var</span> access_token = <span class="string">&#x27;121.c644d8c4*****&#x27;</span> <span class="comment">// accessToken</span></span><br><span class="line"><span class="keyword">var</span> site_id = <span class="string">&#x27;16****&#x27;</span> <span class="comment">// 网址 id</span></span><br><span class="line"><span class="keyword">var</span> dataUrl = <span class="string">&#x27;https://baidu-tongji-api.vercel.app/api?access_token=&#x27;</span> + access_token + <span class="string">&#x27;&amp;site_id=&#x27;</span> + site_id</span><br><span class="line"><span class="keyword">var</span> metrics = <span class="string">&#x27;pv_count&#x27;</span> <span class="comment">// 统计访问次数 PV 填写 &#x27;pv_count&#x27;，统计访客数 UV 填写 &#x27;visitor_count&#x27;，二选一</span></span><br><span class="line"><span class="keyword">var</span> metricsName = (metrics === <span class="string">&#x27;pv_count&#x27;</span> ? <span class="string">&#x27;访问次数&#x27;</span> : (metrics === <span class="string">&#x27;visitor_count&#x27;</span> ? <span class="string">&#x27;访客数&#x27;</span> : <span class="string">&#x27;&#x27;</span>))</span><br><span class="line"><span class="comment">// 这里为了统一颜色选取的是“明暗模式”下的两种字体颜色，也可以自己定义</span></span><br><span class="line"><span class="keyword">var</span> color = <span class="built_in">document</span>.documentElement.getAttribute(<span class="string">&#x27;data-theme&#x27;</span>) === <span class="string">&#x27;light&#x27;</span> ? <span class="string">&#x27;#4c4948&#x27;</span> : <span class="string">&#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 访问地图</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mapChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> script = <span class="built_in">document</span>.createElement(<span class="string">&quot;script&quot;</span>)</span><br><span class="line">  <span class="comment">// let paramUrl = &#x27;&amp;start_date=&#x27; + start_date + &#x27;&amp;end_date=&#x27; + end_date + &#x27;&amp;metrics=&#x27; + metrics + &#x27;&amp;method=visit/district/a&#x27;; // 更换请求地址</span></span><br><span class="line">  <span class="keyword">let</span> paramUrl = <span class="string">&#x27;&amp;start_date=&#x27;</span> + start_date + <span class="string">&#x27;&amp;end_date=&#x27;</span> + end_date + <span class="string">&#x27;&amp;metrics=&#x27;</span> + metrics + <span class="string">&#x27;&amp;method=overview/getDistrictRpt&#x27;</span>;</span><br><span class="line">  fetch(dataUrl + paramUrl).then(<span class="function"><span class="params">data</span> =&gt;</span> data.json()).then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> mapName = data.result.items[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">let</span> mapValue = data.result.items[<span class="number">1</span>]</span><br><span class="line">    <span class="keyword">let</span> mapArr = []</span><br><span class="line">    <span class="keyword">let</span> max = mapValue[<span class="number">0</span>][<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; mapName.length; i++) &#123;</span><br><span class="line">      <span class="comment">// mapArr.push(&#123; name: mapName[i][0].name, value: mapValue[i][0] &#125;)</span></span><br><span class="line">      mapArr.push(&#123; <span class="attr">name</span>: mapName[i][<span class="number">0</span>], <span class="attr">value</span>: mapValue[i][<span class="number">0</span>] &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> mapArrJson = <span class="built_in">JSON</span>.stringify(mapArr)</span><br><span class="line">    script.innerHTML = <span class="string">`</span></span><br><span class="line"><span class="string">      var mapChart = echarts.init(document.getElementById(&#x27;map-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">      var mapOption = &#123;</span></span><br><span class="line"><span class="string">        title: &#123;</span></span><br><span class="line"><span class="string">          text: &#x27;博客访问来源地图&#x27;,</span></span><br><span class="line"><span class="string">          x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">          textStyle: &#123;</span></span><br><span class="line"><span class="string">            color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        tooltip: &#123;</span></span><br><span class="line"><span class="string">          trigger: &#x27;item&#x27;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        visualMap: &#123;</span></span><br><span class="line"><span class="string">          min: 0,</span></span><br><span class="line"><span class="string">          max: <span class="subst">$&#123;max&#125;</span>,</span></span><br><span class="line"><span class="string">          left: &#x27;left&#x27;,</span></span><br><span class="line"><span class="string">          top: &#x27;bottom&#x27;,</span></span><br><span class="line"><span class="string">          text: [&#x27;高&#x27;,&#x27;低&#x27;],</span></span><br><span class="line"><span class="string">          color: [&#x27;#37a2da&#x27;, &#x27;#92d0f9&#x27;],</span></span><br><span class="line"><span class="string">          textStyle: &#123;</span></span><br><span class="line"><span class="string">            color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          calculable: true</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        series: [&#123;</span></span><br><span class="line"><span class="string">          name: &#x27;<span class="subst">$&#123;metricsName&#125;</span>&#x27;,</span></span><br><span class="line"><span class="string">          type: &#x27;map&#x27;,</span></span><br><span class="line"><span class="string">          mapType: &#x27;china&#x27;,</span></span><br><span class="line"><span class="string">          showLegendSymbol: false,</span></span><br><span class="line"><span class="string">          label: &#123;</span></span><br><span class="line"><span class="string">            normal: &#123;</span></span><br><span class="line"><span class="string">              show: false</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            emphasis: &#123;</span></span><br><span class="line"><span class="string">              show: true,</span></span><br><span class="line"><span class="string">              color: &#x27;#617282&#x27;</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          itemStyle: &#123;</span></span><br><span class="line"><span class="string">            normal: &#123;</span></span><br><span class="line"><span class="string">              areaColor: &#x27;rgb(230, 232, 234)&#x27;,</span></span><br><span class="line"><span class="string">              borderColor: &#x27;rgb(255, 255, 255)&#x27;,</span></span><br><span class="line"><span class="string">              borderWidth: 1</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            emphasis: &#123;</span></span><br><span class="line"><span class="string">              areaColor: &#x27;gold&#x27;</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          data: <span class="subst">$&#123;mapArrJson&#125;</span></span></span><br><span class="line"><span class="string">        &#125;]</span></span><br><span class="line"><span class="string">      &#125;;</span></span><br><span class="line"><span class="string">      mapChart.setOption(mapOption);</span></span><br><span class="line"><span class="string">      window.addEventListener(&quot;resize&quot;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">        mapChart.resize();</span></span><br><span class="line"><span class="string">      &#125;);`</span></span><br><span class="line">    <span class="built_in">document</span>.getElementById(<span class="string">&#x27;map-chart&#x27;</span>).after(script);</span><br><span class="line">  &#125;).catch(<span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(error);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 访问趋势</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">trendsChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> script = <span class="built_in">document</span>.createElement(<span class="string">&quot;script&quot;</span>)</span><br><span class="line">  <span class="keyword">let</span> paramUrl = <span class="string">&#x27;&amp;start_date=&#x27;</span> + start_date + <span class="string">&#x27;&amp;end_date=&#x27;</span> + end_date + <span class="string">&#x27;&amp;metrics=&#x27;</span> + metrics + <span class="string">&#x27;&amp;method=trend/time/a&amp;gran=month&#x27;</span></span><br><span class="line">  fetch(dataUrl + paramUrl).then(<span class="function"><span class="params">data</span> =&gt;</span> data.json()).then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> monthArr = []</span><br><span class="line">    <span class="keyword">let</span> monthValueArr = []</span><br><span class="line">    <span class="keyword">let</span> monthName = data.result.items[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">let</span> monthValue = data.result.items[<span class="number">1</span>]</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = monthName.length - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">      monthArr.push(monthName[i][<span class="number">0</span>].substring(<span class="number">0</span>, <span class="number">7</span>).replace(<span class="string">&#x27;/&#x27;</span>, <span class="string">&#x27;-&#x27;</span>))</span><br><span class="line">      monthValueArr.push(monthValue[i][<span class="number">0</span>] !== <span class="string">&#x27;--&#x27;</span> ? monthValue[i][<span class="number">0</span>] : <span class="number">0</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> monthArrJson = <span class="built_in">JSON</span>.stringify(monthArr)</span><br><span class="line">    <span class="keyword">let</span> monthValueArrJson = <span class="built_in">JSON</span>.stringify(monthValueArr)</span><br><span class="line">    script.innerHTML = <span class="string">`</span></span><br><span class="line"><span class="string">      var trendsChart = echarts.init(document.getElementById(&#x27;trends-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">      var trendsOption = &#123;</span></span><br><span class="line"><span class="string">        title: &#123;</span></span><br><span class="line"><span class="string">          text: &#x27;博客访问统计图&#x27;,</span></span><br><span class="line"><span class="string">          x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">          textStyle: &#123;</span></span><br><span class="line"><span class="string">            color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        tooltip: &#123;</span></span><br><span class="line"><span class="string">          trigger: &#x27;axis&#x27;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        xAxis: &#123;</span></span><br><span class="line"><span class="string">          name: &#x27;日期&#x27;,</span></span><br><span class="line"><span class="string">          type: &#x27;category&#x27;,</span></span><br><span class="line"><span class="string">          boundaryGap: false,</span></span><br><span class="line"><span class="string">          nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">            color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          axisTick: &#123;</span></span><br><span class="line"><span class="string">            show: false</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          axisLabel: &#123;</span></span><br><span class="line"><span class="string">            show: true,</span></span><br><span class="line"><span class="string">            color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          axisLine: &#123;</span></span><br><span class="line"><span class="string">            show: true,</span></span><br><span class="line"><span class="string">            lineStyle: &#123;</span></span><br><span class="line"><span class="string">              color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          data: <span class="subst">$&#123;monthArrJson&#125;</span></span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        yAxis: &#123;</span></span><br><span class="line"><span class="string">          name: &#x27;<span class="subst">$&#123;metricsName&#125;</span>&#x27;,</span></span><br><span class="line"><span class="string">          type: &#x27;value&#x27;,</span></span><br><span class="line"><span class="string">          nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">            color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          splitLine: &#123;</span></span><br><span class="line"><span class="string">            show: false</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          axisTick: &#123;</span></span><br><span class="line"><span class="string">            show: false</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          axisLabel: &#123;</span></span><br><span class="line"><span class="string">            show: true,</span></span><br><span class="line"><span class="string">            color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          axisLine: &#123;</span></span><br><span class="line"><span class="string">            show: true,</span></span><br><span class="line"><span class="string">            lineStyle: &#123;</span></span><br><span class="line"><span class="string">              color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        series: [&#123;</span></span><br><span class="line"><span class="string">          name: &#x27;<span class="subst">$&#123;metricsName&#125;</span>&#x27;,</span></span><br><span class="line"><span class="string">          type: &#x27;line&#x27;,</span></span><br><span class="line"><span class="string">          smooth: true,</span></span><br><span class="line"><span class="string">          lineStyle: &#123;</span></span><br><span class="line"><span class="string">              width: 0</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          showSymbol: false,</span></span><br><span class="line"><span class="string">          itemStyle: &#123;</span></span><br><span class="line"><span class="string">            opacity: 1,</span></span><br><span class="line"><span class="string">            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">              offset: 0,</span></span><br><span class="line"><span class="string">              color: &#x27;rgba(128, 255, 165)&#x27;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            &#123;</span></span><br><span class="line"><span class="string">              offset: 1,</span></span><br><span class="line"><span class="string">              color: &#x27;rgba(1, 191, 236)&#x27;</span></span><br><span class="line"><span class="string">            &#125;])</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          areaStyle: &#123;</span></span><br><span class="line"><span class="string">            opacity: 1,</span></span><br><span class="line"><span class="string">            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">              offset: 0,</span></span><br><span class="line"><span class="string">              color: &#x27;rgba(128, 255, 165)&#x27;</span></span><br><span class="line"><span class="string">            &#125;, &#123;</span></span><br><span class="line"><span class="string">              offset: 1,</span></span><br><span class="line"><span class="string">              color: &#x27;rgba(1, 191, 236)&#x27;</span></span><br><span class="line"><span class="string">            &#125;])</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          data: <span class="subst">$&#123;monthValueArrJson&#125;</span>,</span></span><br><span class="line"><span class="string">          markLine: &#123;</span></span><br><span class="line"><span class="string">            data: [&#123;</span></span><br><span class="line"><span class="string">              name: &#x27;平均值&#x27;,</span></span><br><span class="line"><span class="string">              type: &#x27;average&#x27;,</span></span><br><span class="line"><span class="string">              label: &#123;</span></span><br><span class="line"><span class="string">                color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">              &#125;</span></span><br><span class="line"><span class="string">            &#125;]</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;]</span></span><br><span class="line"><span class="string">      &#125;;</span></span><br><span class="line"><span class="string">      trendsChart.setOption(trendsOption);</span></span><br><span class="line"><span class="string">      window.addEventListener(&quot;resize&quot;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">        trendsChart.resize();</span></span><br><span class="line"><span class="string">      &#125;);`</span></span><br><span class="line">    <span class="built_in">document</span>.getElementById(<span class="string">&#x27;trends-chart&#x27;</span>).after(script);</span><br><span class="line">  &#125;).catch(<span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(error);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 访问来源</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sourcesChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> script = <span class="built_in">document</span>.createElement(<span class="string">&quot;script&quot;</span>)</span><br><span class="line">  <span class="keyword">let</span> paramUrl = <span class="string">&#x27;&amp;start_date=&#x27;</span> + start_date + <span class="string">&#x27;&amp;end_date=&#x27;</span> + end_date + <span class="string">&#x27;&amp;metrics=&#x27;</span> + metrics + <span class="string">&#x27;&amp;method=source/all/a&#x27;</span>;</span><br><span class="line">  fetch(dataUrl + paramUrl).then(<span class="function"><span class="params">data</span> =&gt;</span> data.json()).then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> sourcesName = data.result.items[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">let</span> sourcesValue = data.result.items[<span class="number">1</span>]</span><br><span class="line">    <span class="keyword">let</span> sourcesArr = []</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; sourcesName.length; i++) &#123;</span><br><span class="line">      sourcesArr.push(&#123; <span class="attr">name</span>: sourcesName[i][<span class="number">0</span>].name, <span class="attr">value</span>: sourcesValue[i][<span class="number">0</span>] &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> sourcesArrJson = <span class="built_in">JSON</span>.stringify(sourcesArr)</span><br><span class="line">    script.innerHTML = <span class="string">`</span></span><br><span class="line"><span class="string">      var sourcesChart = echarts.init(document.getElementById(&#x27;sources-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">      var sourcesOption = &#123;</span></span><br><span class="line"><span class="string">        title: &#123;</span></span><br><span class="line"><span class="string">          text: &#x27;博客访问来源统计图&#x27;,</span></span><br><span class="line"><span class="string">          x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">          textStyle: &#123;</span></span><br><span class="line"><span class="string">            color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        legend: &#123;</span></span><br><span class="line"><span class="string">          top: &#x27;bottom&#x27;,</span></span><br><span class="line"><span class="string">          textStyle: &#123;</span></span><br><span class="line"><span class="string">            color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        tooltip: &#123;</span></span><br><span class="line"><span class="string">          trigger: &#x27;item&#x27;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        series: [&#123;</span></span><br><span class="line"><span class="string">          name: &#x27;<span class="subst">$&#123;metricsName&#125;</span>&#x27;,</span></span><br><span class="line"><span class="string">          type: &#x27;pie&#x27;,</span></span><br><span class="line"><span class="string">          radius: [30, 80],</span></span><br><span class="line"><span class="string">          center: [&#x27;50%&#x27;, &#x27;50%&#x27;],</span></span><br><span class="line"><span class="string">          roseType: &#x27;area&#x27;,</span></span><br><span class="line"><span class="string">          label: &#123;</span></span><br><span class="line"><span class="string">            color: &#x27;<span class="subst">$&#123;color&#125;</span>&#x27;,</span></span><br><span class="line"><span class="string">            formatter: &quot;&#123;b&#125; : &#123;c&#125; (&#123;d&#125;%)&quot;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          data: <span class="subst">$&#123;sourcesArrJson&#125;</span>,</span></span><br><span class="line"><span class="string">          itemStyle: &#123;</span></span><br><span class="line"><span class="string">            emphasis: &#123;</span></span><br><span class="line"><span class="string">              shadowBlur: 10,</span></span><br><span class="line"><span class="string">              shadowOffsetX: 0,</span></span><br><span class="line"><span class="string">              shadowColor: &#x27;rgba(255, 255, 255, 0.5)&#x27;</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;]</span></span><br><span class="line"><span class="string">      &#125;;</span></span><br><span class="line"><span class="string">      sourcesChart.setOption(sourcesOption);</span></span><br><span class="line"><span class="string">      window.addEventListener(&quot;resize&quot;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">        sourcesChart.resize();</span></span><br><span class="line"><span class="string">      &#125;);`</span></span><br><span class="line">    <span class="built_in">document</span>.getElementById(<span class="string">&#x27;sources-chart&#x27;</span>).after(script);</span><br><span class="line">  &#125;).catch(<span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(error);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">switchVisitChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 这里为了统一颜色选取的是“明暗模式”下的两种字体颜色，也可以自己定义</span></span><br><span class="line">  <span class="keyword">let</span> color = <span class="built_in">document</span>.documentElement.getAttribute(<span class="string">&#x27;data-theme&#x27;</span>) === <span class="string">&#x27;light&#x27;</span> ? <span class="string">&#x27;#4c4948&#x27;</span> : <span class="string">&#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;map-chart&#x27;</span>) &amp;&amp; mapOption) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> mapOptionNew = mapOption</span><br><span class="line">      mapOptionNew.title.textStyle.color = color</span><br><span class="line">      mapOptionNew.visualMap.textStyle.color = color</span><br><span class="line">      mapChart.setOption(mapOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;trends-chart&#x27;</span>) &amp;&amp; trendsOption) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> trendsOptionNew = trendsOption</span><br><span class="line">      trendsOptionNew.title.textStyle.color = color</span><br><span class="line">      trendsOptionNew.xAxis.nameTextStyle.color = color</span><br><span class="line">      trendsOptionNew.yAxis.nameTextStyle.color = color</span><br><span class="line">      trendsOptionNew.xAxis.axisLabel.color = color</span><br><span class="line">      trendsOptionNew.yAxis.axisLabel.color = color</span><br><span class="line">      trendsOptionNew.xAxis.axisLine.lineStyle.color = color</span><br><span class="line">      trendsOptionNew.yAxis.axisLine.lineStyle.color = color</span><br><span class="line">      trendsOptionNew.series[<span class="number">0</span>].markLine.data[<span class="number">0</span>].label.color = color</span><br><span class="line">      trendsChart.setOption(trendsOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;sources-chart&#x27;</span>) &amp;&amp; sourcesOption) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> sourcesOptionNew = sourcesOption</span><br><span class="line">      sourcesOptionNew.title.textStyle.color = color</span><br><span class="line">      sourcesOptionNew.legend.textStyle.color = color</span><br><span class="line">      sourcesOptionNew.series[<span class="number">0</span>].label.color = color</span><br><span class="line">      sourcesChart.setOption(sourcesOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;map-chart&#x27;</span>)) mapChart()</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;trends-chart&#x27;</span>)) trendsChart()</span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;sources-chart&#x27;</span>)) sourcesChart()</span><br><span class="line"></span><br><span class="line"><span class="built_in">document</span>.getElementById(<span class="string">&quot;darkmode&quot;</span>).addEventListener(<span class="string">&quot;click&quot;</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; <span class="built_in">setTimeout</span>(switchVisitChart, <span class="number">100</span>) &#125;)</span><br></pre></td></tr></table></figure></p><p>上面是博主自己三个统计图的代码，小伙伴们可以用以参考，根据自己的需求绘制百度分析统计图。</p><p>更多统计图的自定义属性可以查看 <a href="https://echarts.apache.org/zh/option.html">ECharts 配置项文档</a>，根据自行喜好对 ECharts 统计图进行修改。</p><h2 id="项目部署（可选）"><a href="#项目部署（可选）" class="headerlink" title="项目部署（可选）"></a>项目部署（可选）</h2><p>使用 Github + LeanCloud + Vercel</p><p>整个部署均在云端完成。运行原理：</p><ol><li>数据存储在 LeanCloud 数据库，也可以自行修改源码存储在其他数据库；</li><li>通过 Github Action 更新百度统计的 <code>AccessToken</code> 和 <code>RefreshToken</code>；</li><li>Vercel 部署的 API 从 LeanCloud 获取 <code>AccessToken</code>，再执行请求，获取百度统计数据，返回给前端。</li></ol><h3 id="LeanCloud-部署"><a href="#LeanCloud-部署" class="headerlink" title="LeanCloud 部署"></a>LeanCloud 部署</h3><ol><li><p>新建结构化数据，创建 Class，填写名称为 <code>BaiduToken</code>，设置默认 ACL read 和 write 权限都为所有用户；</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/leancloud_class.png" alt="LeanCloud 新建 BaiduToken Class"></p></li><li><p>点击添加列，分别新增 <code>accessToken</code> 和 <code>refreshToken</code>；</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/leancloud_add_column.png" alt="BaiduToken Class 添加列 "></p></li><li><p>点击新增行，填写前面获取到的 <code>access_token</code> 和 <code>refresh_token</code>；</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/access_token.png" alt="BaiduToken"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/leancloud_add_row.png" alt="BaiduToken Class 添加列 "></p></li></ol><h3 id="GitHub-部署"><a href="#GitHub-部署" class="headerlink" title="GitHub 部署"></a>GitHub 部署</h3><p>由于在 js 文件中请求百度统计 API 获取数据会出现跨域请求的错误。</p><p>博主参考 Akilar 的 <a href="https://akilar.top/posts/1f9c68c9/">基于 Butterfly 主题的首页置顶 gitcalendar</a> 自建 Vercel API 部署教程，以及冰老师的 <a href="https://github.com/Zfour/python_github_calendar_api">python_github_calendar_api</a> 搭建了获取百度统计数据的 Vercel API，力求解决百度统计 API 的跨域请求问题。</p><p><a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/Eurkon/baidu-tongji-api"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://github-readme-stats.eurkon.com/api/pin/?username=Eurkon&repo=baidu-tongji-api&show_owner=true"></a></p><p>虽然 Vercel 的访问应当没有次数限制，但是不排除存在因访问次数过多而限流等限制。所以还是建议各位自建 API。使用 Vercel 部署，完全免费。且无需服务器。</p><ol><li><p>在 baidu-tongji-api 项目 <strong>【Settings】--【Security】--【Secrets】--【Actions】</strong> 中添加以下秘钥：</p><ul><li><code>APIKEY</code>： 百度统计 API Key</li><li><code>SECRETKEY</code>：百度统计 Secret Key</li><li><code>APPID</code>：LeanCloud AppID</li><li><code>APPKEY</code>：LeanCloud AppKey</li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/github_action_secret.png" alt="Github Action Secret "></p></li></ol><p><!-- 2. 添加秘钥完成后可以点击项目 **【Star】** 测试 Github Action 是否能够执行，关注 LeanCloud 是否更新 Token 成功 --></p><h3 id="Vercel-部署"><a href="#Vercel-部署" class="headerlink" title="Vercel 部署"></a>Vercel 部署</h3><p>部署 Vercel 项目后添加 LeanCloud 的环境变量。<img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/vercel_environment_variables.png" alt="Vercel 添加环境变量"></p><p>添加环境变量完成后，将获取到的默认域名替换 <code>census.js</code> 文件中的 <code>dataUrl</code>，如：</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">- var dataUrl = &#x27;https://baidu-tongji-api.vercel.app/api?access_token=&#x27; + access_token + &#x27;&amp;site_id=&#x27; + site_id</span></span><br><span class="line"><span class="addition">+ var dataUrl = &#x27;https://域名/api?access_token=&#x27; + access_token + &#x27;&amp;site_id=&#x27; + site_id</span></span><br></pre></td></tr></table></figure></p><p>如果使用了数据库存储 <code>AccessToken</code>，则可以省略 <code>access_token</code> 参数。</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">- var dataUrl = &#x27;https://baidu-tongji-api.vercel.app/api?access_token=&#x27; + access_token + &#x27;&amp;site_id=&#x27; + site_id</span></span><br><span class="line"><span class="addition">+ var dataUrl = &#x27;https://域名/api?site_id=&#x27; + site_id</span></span><br></pre></td></tr></table></figure></p><h2 id="使用统计图"><a href="#使用统计图" class="headerlink" title="使用统计图"></a>使用统计图</h2><p>在上文新建的 <code>[Blogroot]\source\census\index.md</code> 文件中添加以下内容：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 访问地图 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;map-chart&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 600px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&lt;!-- 访问趋势 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;trends-chart&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 300px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&lt;!-- 访问来源 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;sources-chart&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 300px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">defer</span> <span class="attr">data-pjax</span> <span class="attr">src</span>=<span class="string">&quot;/js/census.js&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br></pre></td></tr></table></figure></p><p>当然也可以在其他页面引入博客访问统计图。</p><h2 id="可能遇到的问题"><a href="#可能遇到的问题" class="headerlink" title="可能遇到的问题"></a>可能遇到的问题</h2><ul><li><p>控制台报错 <code>Uncaught ReferenceError: echarts is not defined</code>。</p><p><strong>解决方案：</strong></p><p>需要在统计图的前引入 <code>echarts.js</code> 文件，最好是在页面的头部引入。</p></li><li><p>控制台报错 <code>Access to fetch at &#39;https://baidu-tongji-api.vercel.app/api?...&#39; from origin &#39;http://...&#39; has been blocked by CORS policy: No &#39;Access-Control-Allow-Origin&#39; header is present on the requested resource. If an opaque response serves your needs, set the request&#39;s mode to &#39;no-cors&#39; to fetch the resource with CORS disabled.</code></p><p><strong>解决方案：</strong></p><p>仍在努力解决跨域问题。</p></li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      
      <comments>https://blog.eurkon.com/post/61763977.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Spark RDD 常用算子</title>
      <link>https://blog.eurkon.com/post/c55e5115.html</link>
      <guid>https://blog.eurkon.com/post/c55e5115.html</guid>
      <pubDate>Fri, 07 May 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Spark-的算子的分类&quot;&gt;&lt;a href=&quot;#Spark-的算子的分类&quot; class=&quot;headerlink&quot; title=&quot;Spark 的算子的分类&quot;&gt;&lt;/a&gt;Spark 的算子的分类&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;从大方向来说，Spark 算子大致可以分为</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Spark-的算子的分类"><a href="#Spark-的算子的分类" class="headerlink" title="Spark 的算子的分类"></a>Spark 的算子的分类</h2><ul><li><p>从大方向来说，Spark 算子大致可以分为以下两类:</p><ul><li><p><strong>Transformation 变换/转换算子</strong>：这种变换并不触发提交作业，完成作业中间过程处理。</p><p>Transformation 操作是延迟计算的，也就是说从一个 RDD 转换生成另一个 RDD 的转换操作不是马上执行，需要等到有 Action 操作的时候才会真正触发运算。</p></li><li><p><strong>Action 行动算子</strong>：这类算子会触发 SparkContext 提交 Job 作业。</p><p>Action 算子会触发 Spark 提交作业（Job），并将数据输出 Spark 系统。</p></li></ul></li><li><p>从小方向来说，Spark 算子大致可以分为以下三类:</p><ul><li><a href="#Value-数据类型的-Transformation-算子">Value 数据类型的 Transformation 算子</a>，这种变换并不触发提交作业，针对处理的数据项是 Value 型的数据。</li><li><a href="#Key-Value-数据类型的-Transformation-算子">Key-Value 数据类型的 Transformation 算子</a>，这种变换并不触发提交作业，针对处理的数据项是 Key-Value 型的数据对。</li><li><a href="#Action-算子">Action 算子</a>，这类算子会触发 SparkContext 提交 Job 作业。</li></ul></li></ul><h2 id="Value-数据类型的-Transformation-算子"><a href="#Value-数据类型的-Transformation-算子" class="headerlink" title="Value 数据类型的 Transformation 算子"></a>Value 数据类型的 Transformation 算子</h2><h3 id="输入分区与输出分区一对一型"><a href="#输入分区与输出分区一对一型" class="headerlink" title="输入分区与输出分区一对一型"></a>输入分区与输出分区一对一型</h3><h4 id="map-算子"><a href="#map-算子" class="headerlink" title="map 算子"></a>map 算子</h4><ul><li><p><strong>说明：</strong>通过将函数应用于此 RDD 的所有元素来返回新的 RDD。</p><p>将原来 RDD 的每个数据项通过 map 中的用户自定义函数 f 映射转变为一个新的元素。源码中 map 算子相当于初始化一个 RDD， 新 RDD 叫做 MappedRDD(this, sc.clean(f))。</p><p>图中每个方框表示一个 RDD 分区，左侧的分区经过用户自定义函数 <code>f:T-&gt;U</code> 映射为右侧的新 RDD 分区。但是，实际只有等到 Action 算子触发后，这个 f 函数才会和其他函数在一个 stage 中对数据进行运算。在图中的第一个分区，数据记录 V1 输入 f，通过 f 转换输出为转换后的分区中的数据记录 V&#39;1。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/map.png" alt="map 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">map</span></span>[<span class="type">U</span>](f: (<span class="type">T</span>) ⇒ <span class="type">U</span>)(<span class="keyword">implicit</span> arg0: <span class="type">ClassTag</span>[<span class="type">U</span>]): <span class="type">RDD</span>[<span class="type">U</span>]</span><br><span class="line"><span class="type">Return</span> a <span class="keyword">new</span> <span class="type">RDD</span> by applying a function to all elements of <span class="keyword">this</span> <span class="type">RDD</span>.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.map(item =&gt; item + <span class="number">1</span>).collect</span><br><span class="line">data.map(_ + <span class="number">1</span>).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">11</span>)</span><br><span class="line">res1: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">11</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="flatMap-算子"><a href="#flatMap-算子" class="headerlink" title="flatMap 算子"></a>flatMap 算子</h4><ul><li><p><strong>说明：</strong>首先向该 RDD 的所有元素应用函数，然后将结果展平，以返回新的 RDD。</p><p>将原来 RDD 中的每个元素通过函数 f 转换为新的元素，并将生成的 RDD 的每个集合中的元素合并为一个集合，内部创建 FlatMappedRDD(this，sc.clean(f))。</p><p>下图表示 RDD 的一个分区，进行 flatMap 函数操作，flatMap 中传入的函数为 <code>f:T-&gt;U</code>， T 和 U 可以是任意的数据类型。将分区中的数据通过用户自定义函数 f 转换为新的数据。外部大方框可以认为是一个 RDD 分区，小方框代表一个集合。 V1、 V2、 V3 在一个集合作为 RDD 的一个数据项，可能存储为数组或其他容器，转换为 V&#39;1、 V&#39;2、 V&#39;3 后，将原来的数组或容器结合拆散，拆散的数据形成为 RDD 中的数据项。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/flat_map.png" alt="flatMap 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">flatMap</span></span>[<span class="type">U</span>](f: (<span class="type">T</span>) ⇒ <span class="type">TraversableOnce</span>[<span class="type">U</span>])(<span class="keyword">implicit</span> arg0: <span class="type">ClassTag</span>[<span class="type">U</span>]): <span class="type">RDD</span>[<span class="type">U</span>]</span><br><span class="line"><span class="type">Return</span> a <span class="keyword">new</span> <span class="type">RDD</span> by first applying a function to all elements of <span class="keyword">this</span> <span class="type">RDD</span>, and then flattening the results.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.flatMap(item =&gt; item to <span class="number">10</span>).collect</span><br><span class="line">data.flatMap(_ to <span class="number">10</span>).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">10</span>)</span><br><span class="line">res1: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">10</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="mapPartitions-算子"><a href="#mapPartitions-算子" class="headerlink" title="mapPartitions 算子"></a>mapPartitions 算子</h4><ul><li><p><strong>说明：</strong>通过将函数应用于此 RDD 的每个分区来返回新的 RDD。</p><p> mapPartitions 函数获取到每个分区的迭代器，在函数中通过这个分区整体的迭代器对整个分区的元素进行操作。内部实现是生成 MapPartitionsRDD。图中的方框代表一个 RDD 分区，用户通过函数 <code>f(iter)=&gt;iter.filter(_&gt;=3)</code> 对分区中所有数据进行过滤，大于和等于 3 的数据保留。一个方块代表一个 RDD 分区，含有 1、2、3 的分区过滤只剩下元素 3。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/map_partitions.png" alt="mapPartitions 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">mapPartitions</span></span>[<span class="type">U</span>](f: (<span class="type">Iterator</span>[<span class="type">T</span>]) ⇒ <span class="type">Iterator</span>[<span class="type">U</span>], preservesPartitioning: <span class="type">Boolean</span> = <span class="literal">false</span>)(<span class="keyword">implicit</span> arg0: <span class="type">ClassTag</span>[<span class="type">U</span>]): <span class="type">RDD</span>[<span class="type">U</span>]</span><br><span class="line"><span class="type">Return</span> a <span class="keyword">new</span> <span class="type">RDD</span> by applying a function to each partition of <span class="keyword">this</span> <span class="type">RDD</span>.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">func</span></span>(it: <span class="type">Iterator</span>[<span class="type">Int</span>]): <span class="type">Iterator</span>[<span class="type">Int</span>] = &#123;</span><br><span class="line">  it.filter(_ &gt;= <span class="number">3</span>)</span><br><span class="line">&#125;</span><br><span class="line">data.mapPartitions(func).collect</span><br><span class="line">data.mapPartitions(_.filter(_ &gt;= <span class="number">3</span>)).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">func: (it: <span class="type">Iterator</span>[<span class="type">Int</span>])<span class="type">Iterator</span>[<span class="type">Int</span>]</span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>)</span><br><span class="line">res1: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="glom-算子"><a href="#glom-算子" class="headerlink" title="glom 算子"></a>glom 算子</h4><ul><li><p><strong>说明：</strong>返回通过将每个分区内的所有元素合并到数组中而创建的 RDD。</p><p>glom 函数将每个分区形成一个数组，内部实现是返回的 GlommedRDD。 图中的每个方框代表一个 RDD 分区。该图表示含有 V1、 V2、 V3 的分区通过函数 glom 形成一数组 Array[(V1),(V2),(V3)]。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/glom.png" alt="glom 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">glom</span></span>(): <span class="type">RDD</span>[<span class="type">Array</span>[<span class="type">T</span>]]</span><br><span class="line"><span class="type">Return</span> an <span class="type">RDD</span> created by coalescing all elements within each partition into an array.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.glom().collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Array</span>[<span class="type">Int</span>]] = <span class="type">Array</span>(<span class="type">Array</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>), <span class="type">Array</span>(<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>), <span class="type">Array</span>(<span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>))</span><br></pre></td></tr></table></figure></p></li></ul><h3 id="输入分区与输出分区多对一型"><a href="#输入分区与输出分区多对一型" class="headerlink" title="输入分区与输出分区多对一型"></a>输入分区与输出分区多对一型</h3><h4 id="union-算子"><a href="#union-算子" class="headerlink" title="union 算子"></a>union 算子</h4><ul><li><p><strong>说明：</strong>返回此 RDD 和另一个 RDD 的联合。</p><p>使用 union 函数时需要保证两个 RDD 元素的数据类型相同，返回的 RDD 数据类型和被合并的 RDD 元素数据类型相同，并不进行去重操作，保存所有元素。如果想去重可以使用 <code>distinct()</code>。同时 Spark 还提供更为简洁的使用 union 的 API，通过 ++ 符号相当于 union 函数操作。</p><p>图中左侧大方框代表两个 RDD，大方框内的小方框代表 RDD 的分区。右侧大方框代表合并后的 RDD，大方框内的小方框代表分区。含有 V1、V2、U1、U2、U3、U4 的 RDD 和含有 V1、V8、U5、U6、U7、U8 的 RDD 合并所有元素形成一个 RDD。V1、V1、V2、V8 形成一个分区，U1、U2、U3、U4、U5、U6、U7、U8 形成一个分区。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/union.png" alt="union 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">union</span></span>(other: <span class="type">RDD</span>[<span class="type">T</span>]): <span class="type">RDD</span>[<span class="type">T</span>]</span><br><span class="line"><span class="type">Return</span> the union of <span class="keyword">this</span> <span class="type">RDD</span> and another one.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data1 = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line"><span class="keyword">val</span> data2 = sc.parallelize(<span class="number">5</span> to <span class="number">15</span>, <span class="number">3</span>)</span><br><span class="line">data1.union(data2).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data1: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">data2: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">1</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">11</span>, <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="cartesian-算子"><a href="#cartesian-算子" class="headerlink" title="cartesian 算子"></a>cartesian 算子</h4><ul><li><p><strong>说明：</strong>返回此 RDD 和另一个 RDD 的进行笛卡尔积运算，即返回 a 和 b 的所有元素对 (a,b) 的 RDD。</p><p>对两个 RDD 内的所有元素进行笛卡尔积操作，该操作不会执行 shuffle 操作。操作后，内部实现返回 CartesianRDD。图中左侧大方框代表两个 RDD，大方框内的小方框代表 RDD 的分区。右侧大方框代表合并后的 RDD，大方框内的小方框代表分区。图中的大方框代表 RDD，大方框中的小方框代表 RDD 分区。例如：V1 和另一个 RDD 中的 W1、W2、Q5 进行笛卡尔积运算形成 (V1,W1)、(V1,W2)、(V1,Q5)。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/cartesian.png" alt="cartesian 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cartesian</span></span>[<span class="type">U</span>](other: <span class="type">RDD</span>[<span class="type">U</span>])(<span class="keyword">implicit</span> arg0: <span class="type">ClassTag</span>[<span class="type">U</span>]): <span class="type">RDD</span>[(<span class="type">T</span>, <span class="type">U</span>)]</span><br><span class="line"><span class="type">Return</span> the <span class="type">Cartesian</span> product of <span class="keyword">this</span> <span class="type">RDD</span> and another one, that is, the <span class="type">RDD</span> of all pairs of elements (a, b) where a is in <span class="keyword">this</span> and b is in other.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data1 = sc.parallelize(<span class="number">1</span> to <span class="number">5</span>, <span class="number">3</span>)</span><br><span class="line"><span class="keyword">val</span> data2 = sc.parallelize(<span class="number">5</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data1.cartesian(data2).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data1: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">data2: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">1</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[(<span class="type">Int</span>, <span class="type">Int</span>)] = <span class="type">Array</span>((<span class="number">1</span>,<span class="number">5</span>), (<span class="number">1</span>,<span class="number">6</span>), (<span class="number">1</span>,<span class="number">7</span>), (<span class="number">1</span>,<span class="number">8</span>), (<span class="number">1</span>,<span class="number">9</span>), (<span class="number">1</span>,<span class="number">10</span>), (<span class="number">2</span>,<span class="number">5</span>), (<span class="number">2</span>,<span class="number">6</span>), (<span class="number">3</span>,<span class="number">5</span>), (<span class="number">3</span>,<span class="number">6</span>), (<span class="number">2</span>,<span class="number">7</span>), (<span class="number">2</span>,<span class="number">8</span>), (<span class="number">3</span>,<span class="number">7</span>), (<span class="number">3</span>,<span class="number">8</span>), (<span class="number">2</span>,<span class="number">9</span>), (<span class="number">2</span>,<span class="number">10</span>), (<span class="number">3</span>,<span class="number">9</span>), (<span class="number">3</span>,<span class="number">10</span>), (<span class="number">4</span>,<span class="number">5</span>), (<span class="number">4</span>,<span class="number">6</span>), (<span class="number">5</span>,<span class="number">5</span>), (<span class="number">5</span>,<span class="number">6</span>), (<span class="number">4</span>,<span class="number">7</span>), (<span class="number">4</span>,<span class="number">8</span>), (<span class="number">5</span>,<span class="number">7</span>), (<span class="number">5</span>,<span class="number">8</span>), (<span class="number">4</span>,<span class="number">9</span>), (<span class="number">4</span>,<span class="number">10</span>), (<span class="number">5</span>,<span class="number">9</span>), (<span class="number">5</span>,<span class="number">10</span>))</span><br></pre></td></tr></table></figure></p></li></ul><h3 id="输入分区与输出分区多对多型"><a href="#输入分区与输出分区多对多型" class="headerlink" title="输入分区与输出分区多对多型"></a>输入分区与输出分区多对多型</h3><h4 id="groupBy-算子"><a href="#groupBy-算子" class="headerlink" title="groupBy 算子"></a>groupBy 算子</h4><ul><li><p><strong>说明：</strong>返回分组元素的 RDD。每个组由一个键和映射到该键的一系列元素组成。不能保证每个组中元素的顺序，并且每次生成的 RDD 时甚至可能会有所不同。</p><p>将元素通过函数生成相应的 Key，数据就转化为 Key-Value 格式，之后将 Key 相同的元素分为一组。函数实现如下：将用户函数预处理，对数据 map 进行函数操作，最后再进行 groupByKey 分组操作。<code>this.map(t =&gt; (cleanF(t), t)).groupByKey(p)</code>，其中， p 确定了分区个数和分区函数，也就决定了并行化的程度。</p><p>图中方框代表一个 RDD 分区，相同 key 的元素合并到一个组。例如 V1 和 V2 合并为 V，Value 为 V1,V2。形成 V,Seq(V1,V2)。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/group_by.png" alt="groupBy 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">groupBy</span></span>[<span class="type">K</span>](f: (<span class="type">T</span>) ⇒ <span class="type">K</span>)(<span class="keyword">implicit</span> kt: <span class="type">ClassTag</span>[<span class="type">K</span>]): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">Iterable</span>[<span class="type">T</span>])]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">groupBy</span></span>[<span class="type">K</span>](f: (<span class="type">T</span>) ⇒ <span class="type">K</span>, numPartitions: <span class="type">Int</span>)(<span class="keyword">implicit</span> kt: <span class="type">ClassTag</span>[<span class="type">K</span>]): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">Iterable</span>[<span class="type">T</span>])]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">groupBy</span></span>[<span class="type">K</span>](f: (<span class="type">T</span>) ⇒ <span class="type">K</span>, p: <span class="type">Partitioner</span>)(<span class="keyword">implicit</span> kt: <span class="type">ClassTag</span>[<span class="type">K</span>], ord: <span class="type">Ordering</span>[<span class="type">K</span>] = <span class="literal">null</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">Iterable</span>[<span class="type">T</span>])]</span><br><span class="line"><span class="type">Return</span> an <span class="type">RDD</span> of grouped elements. <span class="type">Each</span> group consists of a key and a sequence of elements mapping to that key. <span class="type">The</span> ordering of elements within each group is not guaranteed, and may even differ each time the resulting <span class="type">RDD</span> is evaluated.</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">groupByKey</span></span>(): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">Iterable</span>[<span class="type">V</span>])]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">groupByKey</span></span>(numPartitions: <span class="type">Int</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">Iterable</span>[<span class="type">V</span>])]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">groupByKey</span></span>(partitioner: <span class="type">Partitioner</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">Iterable</span>[<span class="type">V</span>])]</span><br><span class="line"><span class="type">Group</span> the values <span class="keyword">for</span> each key in the <span class="type">RDD</span> into a single sequence. <span class="type">Hash</span>-partitions the resulting <span class="type">RDD</span> <span class="keyword">with</span> the existing partitioner/parallelism level. <span class="type">The</span> ordering of elements within each group is not guaranteed, and may even differ each time the resulting <span class="type">RDD</span> is evaluated.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data1 = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data1.groupBy(_ % <span class="number">2</span>).collect  <span class="comment">// 分成两组</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">val</span> data2 = sc.parallelize(<span class="type">Array</span>(<span class="string">&quot;a&quot;</span>, <span class="string">&quot;b&quot;</span>, <span class="string">&quot;c&quot;</span>, <span class="string">&quot;aa&quot;</span>, <span class="string">&quot;bb&quot;</span>, <span class="string">&quot;cc&quot;</span>, <span class="string">&quot;aaa&quot;</span>, <span class="string">&quot;bbb&quot;</span>, <span class="string">&quot;ccc&quot;</span>), <span class="number">1</span>)</span><br><span class="line"><span class="keyword">val</span> data3 = data2.keyBy(_.length)  <span class="comment">// 给 value 加上 key，key 为对应 string 的长度</span></span><br><span class="line">data3.groupByKey.collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data1: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[(<span class="type">Int</span>, <span class="type">Iterable</span>[<span class="type">Int</span>])] = <span class="type">Array</span>((<span class="number">0</span>,<span class="type">CompactBuffer</span>(<span class="number">4</span>, <span class="number">6</span>, <span class="number">2</span>, <span class="number">8</span>, <span class="number">10</span>)), (<span class="number">1</span>,<span class="type">CompactBuffer</span>(<span class="number">1</span>, <span class="number">3</span>, <span class="number">7</span>, <span class="number">9</span>, <span class="number">5</span>)))</span><br><span class="line"></span><br><span class="line">data2: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">String</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">1</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">data3: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">Int</span>, <span class="type">String</span>)] = <span class="type">MapPartitionsRDD</span>[<span class="number">2</span>] at keyBy at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res1: <span class="type">Array</span>[(<span class="type">Int</span>, <span class="type">Iterable</span>[<span class="type">String</span>])] = <span class="type">Array</span>((<span class="number">1</span>,<span class="type">CompactBuffer</span>(a, b, c)), (<span class="number">3</span>,<span class="type">CompactBuffer</span>(aaa, bbb, ccc)), (<span class="number">2</span>,<span class="type">CompactBuffer</span>(aa, bb, cc)))</span><br></pre></td></tr></table></figure></p></li></ul><h3 id="输出分区为输入分区子集型"><a href="#输出分区为输入分区子集型" class="headerlink" title="输出分区为输入分区子集型"></a>输出分区为输入分区子集型</h3><h4 id="filter-算子"><a href="#filter-算子" class="headerlink" title="filter 算子"></a>filter 算子</h4><ul><li><p><strong>说明：</strong>返回仅包含满足条件的元素的新 RDD。</p><p>filter 函数功能是对元素进行过滤，对每个元素应用 f 函 数，返回值为 true 的元素在 RDD 中保留，返回值为 false 的元素将被过滤掉。内部实现相当于生成 <code>FilteredRDD(this，sc.clean(f))</code>。图中每个方框代表一个 RDD 分区， T 可以是任意的类型。通过用户自定义的过滤函数 f，对每个数据项操作，将满足条件、返回结果为 true 的数据项保留。例如，过滤掉 V2 和 V3 保留了 V1，为区分命名为 V&#39;1。</p><p>下面代码为函数的本质实现：<code>def filter(f:T=&gt;Boolean):RDD[T]=newFilteredRDD(this,sc.clean(f))</code></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/filter.png" alt="filter 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">filter</span></span>(f: (<span class="type">T</span>) ⇒ <span class="type">Boolean</span>): <span class="type">RDD</span>[<span class="type">T</span>]</span><br><span class="line"><span class="type">Return</span> a <span class="keyword">new</span> <span class="type">RDD</span> containing only the elements that satisfy a predicate.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.filter(_ % <span class="number">2</span> == <span class="number">0</span>).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">10</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="distinct-算子"><a href="#distinct-算子" class="headerlink" title="distinct 算子"></a>distinct 算子</h4><ul><li><p><strong>说明：</strong>返回一个包含该 RDD 中不同元素的新 RDD。</p><p>distinct 将 RDD 中的元素进行去重操作。图中的每个方框代表一个 RDD 分区，通过 distinct 函数，将数据去重。例如，重复数据 V1、V1 去重后只保留一份 V1。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/distinct.png" alt="distinct 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">distinct</span></span>(): <span class="type">RDD</span>[<span class="type">T</span>]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">distinct</span></span>(numPartitions: <span class="type">Int</span>)(<span class="keyword">implicit</span> ord: <span class="type">Ordering</span>[<span class="type">T</span>] = <span class="literal">null</span>): <span class="type">RDD</span>[<span class="type">T</span>]</span><br><span class="line"><span class="type">Return</span> a <span class="keyword">new</span> <span class="type">RDD</span> containing the distinct elements in <span class="keyword">this</span> <span class="type">RDD</span>.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="type">Array</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">1</span>), <span class="number">3</span>)</span><br><span class="line">data.distinct.collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">3</span>, <span class="number">1</span>, <span class="number">2</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="subtract-算子"><a href="#subtract-算子" class="headerlink" title="subtract 算子"></a>subtract 算子</h4><ul><li><p><strong>说明：</strong>返回一个新 RDD，其中的元素在第一个 RDD 有，第二个 RDD 没有。</p><p>subtract 相当于进行集合的差操作，RDD1 去除 RDD1 和 RDD2 交集中的所有元素。图中左侧的大方框代表两个 RDD，大方框内的小方框代表 RDD 的分区。右侧大方框代表合并后的 RDD，大方框内的小方框代表分区。V1 在两个 RDD 中均有，根据差集运算规则，新 RDD 不保留，V2 在第一个 RDD 有，第二个 RDD 没有，则在新 RDD 元素中包含 V2。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/subtract.png" alt="subtract 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">subtract</span></span>(other: <span class="type">RDD</span>[<span class="type">T</span>]): <span class="type">RDD</span>[<span class="type">T</span>]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">subtract</span></span>(other: <span class="type">RDD</span>[<span class="type">T</span>], numPartitions: <span class="type">Int</span>): <span class="type">RDD</span>[<span class="type">T</span>]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">subtract</span></span>(other: <span class="type">RDD</span>[<span class="type">T</span>], p: <span class="type">Partitioner</span>)(<span class="keyword">implicit</span> ord: <span class="type">Ordering</span>[<span class="type">T</span>] = <span class="literal">null</span>): <span class="type">RDD</span>[<span class="type">T</span>]</span><br><span class="line"><span class="type">Return</span> an <span class="type">RDD</span> <span class="keyword">with</span> the elements from <span class="keyword">this</span> that are not in other.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data1 = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line"><span class="keyword">val</span> data2 = sc.parallelize(<span class="number">1</span> to <span class="number">5</span>, <span class="number">3</span>)</span><br><span class="line">data1.subtract(data2).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data1: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">data2: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">1</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">6</span>, <span class="number">9</span>, <span class="number">7</span>, <span class="number">10</span>, <span class="number">8</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="sample-算子"><a href="#sample-算子" class="headerlink" title="sample 算子"></a>sample 算子</h4><ul><li><p><strong>说明：</strong>返回此 RDD 的采样子集。</p><p> sample 将 RDD 这个集合内的元素进行采样，获取所有元素的子集。用户可以设定是否有放回的抽样、百分比、随机种子，进而决定采样方式。内部实现是生成 SampledRDD(withReplacement, fraction, seed)。图中的每个方框是一个 RDD 分区。通过 sample 函数， 采样 50% 的数据。V1、V2、U1、U2、U3、U4 采样出数据 V1 和 U1、U2 形成新的 RDD。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/sample.png" alt="sample 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sample</span></span>(withReplacement: <span class="type">Boolean</span>, fraction: <span class="type">Double</span>, seed: <span class="type">Long</span> = <span class="type">Utils</span>.random.nextLong): <span class="type">RDD</span>[<span class="type">T</span>]</span><br><span class="line"><span class="type">Return</span> a sampled subset of <span class="keyword">this</span> <span class="type">RDD</span>.</span><br><span class="line"></span><br><span class="line">withReplacement：can elements be sampled multiple times (replaced when sampled out)</span><br><span class="line">可以多次采样元素（采样时替换），<span class="literal">true</span> 表示有放回抽样，<span class="literal">false</span> 表示无放回抽样，</span><br><span class="line"></span><br><span class="line">fraction：expected size of the sample as a fraction of <span class="keyword">this</span> <span class="type">RDD</span><span class="symbol">&#x27;s</span> size without replacement: probability that each element is chosen; fraction must be [<span class="number">0</span>, <span class="number">1</span>] <span class="keyword">with</span> replacement: expected number of times each element is chosen; fraction must be greater than or equal to <span class="number">0</span></span><br><span class="line">样本的预期大小，占该 <span class="type">RDD</span> 大小的一部分，无需替换：选择每个元素的概率；分数必须为 [<span class="number">0</span>，<span class="number">1</span>]，并带有替换：选择每个元素的预期次数；小数必须大于或等于 <span class="number">0</span></span><br><span class="line"></span><br><span class="line">seed：seed <span class="keyword">for</span> the random number generator</span><br><span class="line">随机数生成器的种子</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.sample(<span class="literal">true</span>, <span class="number">0.5</span>, <span class="number">9</span>).collect</span><br><span class="line">data.sample(<span class="literal">false</span>, <span class="number">0.5</span>, <span class="number">9</span>).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">1</span>, <span class="number">1</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">9</span>)</span><br><span class="line">res1: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">9</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h3 id="Cache-型"><a href="#Cache-型" class="headerlink" title="Cache 型"></a>Cache 型</h3><h4 id="cache-算子"><a href="#cache-算子" class="headerlink" title="cache 算子"></a>cache 算子</h4><ul><li><p><strong>说明：</strong>使用默认存储级别（MEMORY_ONLY）缓存该 RDD。</p><p>cache 将 RDD 元素从磁盘缓存到内存。相当于 persist(MEMORY_ONLY) 函数的功能。图中每个方框代表一个 RDD 分区，左侧相当于数据分区都存储在磁盘，通过 cache 算子将数据缓存在内存。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/cache.png" alt="cache 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cache</span></span>(): <span class="type">RDD</span>.<span class="keyword">this</span>.<span class="keyword">type</span></span><br><span class="line"><span class="type">Persist</span> <span class="keyword">this</span> <span class="type">RDD</span> <span class="keyword">with</span> the <span class="keyword">default</span> storage level (<span class="type">MEMORY_ONLY</span>).</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.cache()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: data.<span class="keyword">type</span> = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br></pre></td></tr></table></figure></p></li></ul><h4 id="persist-算子"><a href="#persist-算子" class="headerlink" title="persist 算子"></a>persist 算子</h4><ul><li><p><strong>说明：</strong>设置此 RDD 的存储级别，以在第一次计算它之后将其值持久化到各个操作中。如果 RDD 尚未设置存储级别，则只能用于分配新的存储级别。本地检查点是一个例外。</p><p>persist 函数对 RDD 进行缓存操作。数据缓存在哪里依据 <code>StorageLevel</code> 这个枚举类型进行确定。有以下几种类型的组合，<code>DISK</code> 代表磁盘，<code>MEMORY</code> 代表内存，<code>SER</code> 代表数据是否进行序列化存储。</p><p>下面为函数定义，<code>StorageLevel</code> 是枚举类型，代表存储模式，用户可以按需进行选择。<code>persist(newLevel:StorageLevel)</code>，下面列出 persist 函数可以进行缓存的模式。例如，<code>MEMORY_AND_DISK_SER</code> 代表数据可以存储在内存和磁盘，并且以序列化的方式存储，其他同理。</p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StorageLevel</span> <span class="title">private</span>(<span class="params"></span></span></span><br><span class="line"><span class="class"><span class="params">    private var _useDisk: <span class="type">Boolean</span>, //是否使用磁盘</span></span></span><br><span class="line"><span class="class"><span class="params">    private var _useMemory: <span class="type">Boolean</span>, //是否使用内存</span></span></span><br><span class="line"><span class="class"><span class="params">    private var _useOffHeap: <span class="type">Boolean</span>, //是否使用堆外内存</span></span></span><br><span class="line"><span class="class"><span class="params">    private var _deserialized: <span class="type">Boolean</span>, //是否反序列化</span></span></span><br><span class="line"><span class="class"><span class="params">    private var _replication: <span class="type">Int</span> = 1</span>) <span class="comment">//备份因子，默认为 1</span></span></span><br><span class="line"><span class="class">  <span class="keyword">extends</span> <span class="title">Externalizable</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">StorageLevel</span> </span>&#123;</span><br><span class="line">  <span class="keyword">val</span> <span class="type">NONE</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">DISK_ONLY</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">DISK_ONLY_2</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="number">2</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">MEMORY_ONLY</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">false</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">true</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">MEMORY_ONLY_2</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">false</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">true</span>, <span class="number">2</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">MEMORY_ONLY_SER</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">false</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">MEMORY_ONLY_SER_2</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">false</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="number">2</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">MEMORY_AND_DISK</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">true</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">true</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">MEMORY_AND_DISK_2</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">true</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">true</span>, <span class="number">2</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">MEMORY_AND_DISK_SER</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">true</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">MEMORY_AND_DISK_SER_2</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">true</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="number">2</span>)</span><br><span class="line">  <span class="keyword">val</span> <span class="type">OFF_HEAP</span> = <span class="keyword">new</span> <span class="type">StorageLevel</span>(<span class="literal">true</span>, <span class="literal">true</span>, <span class="literal">true</span>, <span class="literal">false</span>, <span class="number">1</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>下图中方框代表 RDD 分区。<code>disk</code> 代表存储在磁盘，<code>mem</code> 代表存储在内存。数据最初全部存储在磁盘，通过 <code>persist(MEMORY_AND_DISK)</code> 将数据缓存到内存，但是有的分区无法容纳在内存，将含有 V1、 V2、 V3 的 RDD 存储到磁盘，将含有 U1，U2 的 RDD 仍旧存储在内存。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/persist.png" alt="persist 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">persist</span></span>(): <span class="type">RDD</span>.<span class="keyword">this</span>.<span class="keyword">type</span></span><br><span class="line"><span class="type">Persist</span> <span class="keyword">this</span> <span class="type">RDD</span> <span class="keyword">with</span> the <span class="keyword">default</span> storage level (<span class="type">MEMORY_ONLY</span>).</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">persist</span></span>(newLevel: <span class="type">StorageLevel</span>): <span class="type">RDD</span>.<span class="keyword">this</span>.<span class="keyword">type</span></span><br><span class="line"><span class="type">Set</span> <span class="keyword">this</span> <span class="type">RDD</span><span class="symbol">&#x27;s</span> storage level to persist its values across operations after the first time it is computed. <span class="type">This</span> can only be used to assign a <span class="keyword">new</span> storage level <span class="keyword">if</span> the <span class="type">RDD</span> does not have a storage level set yet. <span class="type">Local</span> checkpointing is an exception.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.persist(<span class="type">StorageLevel</span>.<span class="type">MEMORY_AND_DISK</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: data.<span class="keyword">type</span> = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br></pre></td></tr></table></figure></p></li></ul><h2 id="Key-Value-数据类型的-Transformation-算子"><a href="#Key-Value-数据类型的-Transformation-算子" class="headerlink" title="Key-Value 数据类型的 Transformation 算子"></a>Key-Value 数据类型的 Transformation 算子</h2><h3 id="输入分区与输出分区一对一"><a href="#输入分区与输出分区一对一" class="headerlink" title="输入分区与输出分区一对一"></a>输入分区与输出分区一对一</h3><h4 id="mapValues-算子"><a href="#mapValues-算子" class="headerlink" title="mapValues 算子"></a>mapValues 算子</h4><ul><li><p><strong>说明：</strong>在不更改键的情况下，通过映射函数传递键值对 RDD 中的每个值；这也保留了原始 RDD 的分区。</p><p>针对 (Key, Value) 型数据中的 Value 进行 Map 操作，而不对 Key 进行处理。图中的方框代表 RDD 分区。 <code>a=&gt;a+2</code> 代表对 (V1, 1) 这样的 Key-Value 数据对，数据只对 Value 中的 1 进行加 2 操作，返回结果为 3。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/map_values.png" alt="mapValues 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">mapValues</span></span>[<span class="type">U</span>](f: (<span class="type">V</span>) ⇒ <span class="type">U</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">U</span>)]</span><br><span class="line"><span class="type">Pass</span> each value in the key-value pair <span class="type">RDD</span> through a map function without changing the keys; <span class="keyword">this</span> also retains the original <span class="type">RDD</span><span class="symbol">&#x27;s</span> partitioning.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="type">Array</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>), <span class="number">3</span>)</span><br><span class="line">data.map(x =&gt; (x, x)).mapValues(a =&gt; (a + <span class="number">2</span>)).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[(<span class="type">Int</span>, <span class="type">Int</span>)] = <span class="type">Array</span>((<span class="number">1</span>,<span class="number">3</span>), (<span class="number">2</span>,<span class="number">4</span>), (<span class="number">3</span>,<span class="number">5</span>), (<span class="number">4</span>,<span class="number">6</span>), (<span class="number">5</span>,<span class="number">7</span>), (<span class="number">6</span>,<span class="number">8</span>))</span><br></pre></td></tr></table></figure></p></li></ul><h3 id="对单个-RDD-或两个-RDD-聚合"><a href="#对单个-RDD-或两个-RDD-聚合" class="headerlink" title="对单个 RDD 或两个 RDD 聚合"></a>对单个 RDD 或两个 RDD 聚合</h3><p><strong>单个 RDD 聚合</strong></p><h4 id="combineByKey-算子"><a href="#combineByKey-算子" class="headerlink" title="combineByKey 算子"></a>combineByKey 算子</h4><ul><li><p><strong>说明：</strong>CombineByKeyWithClassTag 的简化版本，它使用现有的分区程序/并行度级别对生成的 RDD 进行哈希分区。此方法是为了向后兼容。它不向混洗提供组合器类标签信息。</p><p>例如，相当于将元素为 (Int， Int) 的 RDD 转变为了 (Int, Seq[Int]) 类型元素的 RDD。图中的方框代表 RDD 分区。如图，通过 combineByKey，将 (V1,2)， (V1,1) 数据合并为 (V1, Seq(2, 1))。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/combine_by_key.png" alt="combineByKey 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">combineByKey</span></span>[<span class="type">C</span>](createCombiner: (<span class="type">V</span>) ⇒ <span class="type">C</span>, mergeValue: (<span class="type">C</span>, <span class="type">V</span>) ⇒ <span class="type">C</span>, mergeCombiners: (<span class="type">C</span>, <span class="type">C</span>) ⇒ <span class="type">C</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">C</span>)]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">combineByKey</span></span>[<span class="type">C</span>](createCombiner: (<span class="type">V</span>) ⇒ <span class="type">C</span>, mergeValue: (<span class="type">C</span>, <span class="type">V</span>) ⇒ <span class="type">C</span>, mergeCombiners: (<span class="type">C</span>, <span class="type">C</span>) ⇒ <span class="type">C</span>, numPartitions: <span class="type">Int</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">C</span>)]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">combineByKey</span></span>[<span class="type">C</span>](createCombiner: (<span class="type">V</span>) ⇒ <span class="type">C</span>, mergeValue: (<span class="type">C</span>, <span class="type">V</span>) ⇒ <span class="type">C</span>, mergeCombiners: (<span class="type">C</span>, <span class="type">C</span>) ⇒ <span class="type">C</span>, partitioner: <span class="type">Partitioner</span>, mapSideCombine: <span class="type">Boolean</span> = <span class="literal">true</span>, serializer: <span class="type">Serializer</span> = <span class="literal">null</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">C</span>)]</span><br><span class="line"></span><br><span class="line">createCombiner，createCombiner, which turns a <span class="type">V</span> into a <span class="type">C</span> (e.g., creates a one-element <span class="type">Array</span>)</span><br><span class="line">将 <span class="type">V</span> 变成 <span class="type">C</span>（例如，创建一个元素列表），<span class="type">C</span> 不存在的情况下，比如通过 <span class="type">V</span> 创建 seq <span class="type">C</span>。</span><br><span class="line"></span><br><span class="line">mergeValue, to merge a <span class="type">V</span> into a <span class="type">C</span> (e.g., adds it to the end of a <span class="type">Array</span>)</span><br><span class="line">将 <span class="type">V</span> 合并为 <span class="type">C</span>（例如，将其添加到列表的末尾），当 <span class="type">C</span> 已经存在的情况下，需要 merge，比如把 item <span class="type">V</span> 加到 seq <span class="type">C</span> 中，或者叠加。</span><br><span class="line"></span><br><span class="line">mergeCombiners, to combine two <span class="type">C</span><span class="symbol">&#x27;s</span> into a single one.</span><br><span class="line">将两个 <span class="type">C</span> 合并为一个。</span><br><span class="line"></span><br><span class="line">mapSideCombine <span class="type">Boolean</span> = <span class="literal">true</span></span><br><span class="line">为了减小传输量，很多 combine 可以在 map 端先做，比如叠加，可以先在一个 partition 中把所有相同的 key 的 value 叠加，再 shuffle。</span><br><span class="line"></span><br><span class="line">serializerClass： <span class="type">String</span> = <span class="literal">null</span></span><br><span class="line">传输需要序列化，用户可以自定义序列化类。</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">88</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">95</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">91</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">93</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">95</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">98</span>)))</span><br><span class="line">data.combineByKey(</span><br><span class="line">    (v) =&gt; (v, <span class="number">1</span>),</span><br><span class="line">    (c: (<span class="type">Int</span>, <span class="type">Int</span>), v) =&gt; (c._1 + v, c._2 + <span class="number">1</span>),</span><br><span class="line">    (c1: (<span class="type">Int</span>, <span class="type">Int</span>), c2: (<span class="type">Int</span>, <span class="type">Int</span>)) =&gt; (c1._1 + c2._1, c1._2 + c2._2)</span><br><span class="line">).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[(<span class="type">String</span>, (<span class="type">Int</span>, <span class="type">Int</span>))] = <span class="type">Array</span>((a,(<span class="number">274</span>,<span class="number">3</span>)), (b,(<span class="number">286</span>,<span class="number">3</span>)))</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="reduceByKey-算子"><a href="#reduceByKey-算子" class="headerlink" title="reduceByKey 算子"></a>reduceByKey 算子</h4><ul><li><p><strong>说明：</strong>使用关联和可交换的归约函数合并每个键的值。在将结果发送给 reducer 之前，这还将在每个 Mapper 上本地执行合并，这与 MapReduce 中的 combiner 类似。输出将使用现有分区/并行级别进行哈希分区。</p><p>reduceByKey 是比 combineByKey 更简单的一种情况，只是两个值合并成一个值，(Int, Int V) to (Int, Int C)，比如叠加。所以 createCombiner reduceByKey 很简单，就是直接返回 v，而 mergeValue 和 mergeCombiners 逻辑是相同的，没有区别。图中的方框代表 RDD 分区。通过用户自定义函数 (A,B) =&gt; (A + B) 函数，将相同 key 的数据 (V1,2) 和 (V1,1) 的 value 相加运算，结果为 (V1,3)。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/reduce_by_key.png" alt="reduceByKey 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">reduceByKey</span></span>(func: (<span class="type">V</span>, <span class="type">V</span>) ⇒ <span class="type">V</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">V</span>)]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">reduceByKey</span></span>(func: (<span class="type">V</span>, <span class="type">V</span>) ⇒ <span class="type">V</span>, numPartitions: <span class="type">Int</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">V</span>)]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">reduceByKey</span></span>(partitioner: <span class="type">Partitioner</span>, func: (<span class="type">V</span>, <span class="type">V</span>) ⇒ <span class="type">V</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">V</span>)]</span><br><span class="line"><span class="type">Merge</span> the values <span class="keyword">for</span> each key using an associative and commutative reduce function.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">3</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">4</span>)))</span><br><span class="line">data.reduceByKey((x, y) =&gt; x + y).collect</span><br><span class="line">data.reduceByKey(_ + _).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">Int</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">Array</span>((b,<span class="number">7</span>), (a,<span class="number">3</span>))</span><br><span class="line">res1: <span class="type">Array</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">Array</span>((b,<span class="number">7</span>), (a,<span class="number">3</span>))</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="partitionBy-算子"><a href="#partitionBy-算子" class="headerlink" title="partitionBy 算子"></a>partitionBy 算子</h4><ul><li><p><strong>说明：</strong>返回使用指定分区程序分区的 RDD 的副本。</p><p>如果原有 RDD 的分区器和现有分区器（partitioner）一致，则不重分区，如果不一致，则相当于根据分区器生成一个新的 ShuffledRDD。图中的方框代表 RDD 分区。通过新的分区策略将原来在不同分区的 V1、 V2 数据都合并到了一个分区。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/partition_by.png" alt="partitionBy 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">partitionBy</span></span>(partitioner: <span class="type">Partitioner</span>): <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">V</span>)]</span><br><span class="line"><span class="type">Return</span> a copy of the <span class="type">RDD</span> partitioned using the specified partitioner.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="type">Array</span>(<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">3</span>), (<span class="string">&quot;c&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;e&quot;</span>, <span class="number">5</span>), <span class="number">2</span>)</span><br><span class="line">data.partitionBy(<span class="keyword">new</span> <span class="type">HashPartitioner</span>(<span class="number">4</span>)).glom.collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">Int</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Array</span>[(<span class="type">String</span>, <span class="type">Int</span>)]] = <span class="type">Array</span>(<span class="type">Array</span>((a,<span class="number">2</span>), (c,<span class="number">1</span>)), <span class="type">Array</span>((b,<span class="number">2</span>), (e,<span class="number">5</span>)), <span class="type">Array</span>(), <span class="type">Array</span>((a,<span class="number">1</span>), (b,<span class="number">3</span>)))</span><br></pre></td></tr></table></figure></p></li></ul><p><strong>两个 RDD 聚合</strong></p><h4 id="cogroup-算子"><a href="#cogroup-算子" class="headerlink" title="cogroup 算子"></a>cogroup 算子</h4><ul><li><p><strong>说明：</strong>对在两个 RDD 中的 Key-Value 类型的元素，每个 RDD 相同 Key 的元素分别聚合为一个集合，并且返回两个 RDD 中对应 Key 的元素集合的迭代器。</p><p>其中，Key 和 Value，Value 是两个 RDD 下相同 Key 的两个数据集合的迭代器所构成的元组。图中的大方框代表 RDD，大方框内的小方框代表 RDD 中的分区。将 RDD1 中的数据 (U,1)、(U1,2) 和 RDD2 中的数据 (U1，2) 合并为 (U1,((1,2),(2)))。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/cogroup.png" alt="cogroup 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cogroup</span></span>[<span class="type">W1</span>, <span class="type">W2</span>](other1: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W1</span>)], other2: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W2</span>)], numPartitions: <span class="type">Int</span>): <span class="type">RDD</span>[(<span class="type">K</span>, (<span class="type">Iterable</span>[<span class="type">V</span>], <span class="type">Iterable</span>[<span class="type">W1</span>], <span class="type">Iterable</span>[<span class="type">W2</span>]))]</span><br><span class="line"><span class="type">For</span> each key k in <span class="keyword">this</span> or other1 or other2, <span class="keyword">return</span> a resulting <span class="type">RDD</span> that contains a tuple <span class="keyword">with</span> the list of values <span class="keyword">for</span> that key in <span class="keyword">this</span>, other1 and other2.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data1 = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">2</span>)))</span><br><span class="line"><span class="keyword">val</span> data2 = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">3</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">4</span>)))</span><br><span class="line">data1.cogroup(data2).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data1: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">data2: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">1</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[(<span class="type">String</span>, (<span class="type">Iterable</span>[<span class="type">Int</span>], <span class="type">Iterable</span>[<span class="type">Int</span>]))] = <span class="type">Array</span>((a,(<span class="type">CompactBuffer</span>(<span class="number">1</span>, <span class="number">2</span>),<span class="type">CompactBuffer</span>(<span class="number">4</span>, <span class="number">3</span>))))</span><br></pre></td></tr></table></figure></p></li></ul><h3 id="连接"><a href="#连接" class="headerlink" title="连接"></a>连接</h3><h4 id="join-算子"><a href="#join-算子" class="headerlink" title="join 算子"></a>join 算子</h4><ul><li><p><strong>说明：</strong>返回一个 RDD，其中包含所有成对的元素。</p><p>join 对两个需要连接的 RDD 进行 cogroup 函数操作，将相同 key 的数据能够放到一个分区，在 cogroup 操作之后形成的新 RDD 对每个 key 下的元素进行笛卡尔积的操作，返回的结果再展平，对应 key 下的所有元组形成一个集合。最后返回 RDD[(K， (V， W))]。</p><p>下面代码为 join 的函数实现，本质是通过 cogroup 算子先进行协同划分，再通过 flatMapValues 将合并的数据打散。</p><p><code>this.cogroup(other, partitioner).flatMapValues&#123;case(vs, ws) =&gt; for(v&lt;-vs; w&lt;-ws) yield(v, w) &#125;</code></p><p>下图是对两个 RDD 的 join 操作示意图。大方框代表 RDD，小方框代表 RDD 中的分区。函数对相同 key 的元素，如 V1 为 key 做连接后结果为 (V1,(1,1)) 和 (V1,(1,2))。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/join.png" alt="join 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">join</span></span>[<span class="type">W</span>](other: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W</span>)]): <span class="type">RDD</span>[(<span class="type">K</span>, (<span class="type">V</span>, <span class="type">W</span>))]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">join</span></span>[<span class="type">W</span>](other: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W</span>)], numPartitions: <span class="type">Int</span>): <span class="type">RDD</span>[(<span class="type">K</span>, (<span class="type">V</span>, <span class="type">W</span>))]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">join</span></span>[<span class="type">W</span>](other: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W</span>)], partitioner: <span class="type">Partitioner</span>): <span class="type">RDD</span>[(<span class="type">K</span>, (<span class="type">V</span>, <span class="type">W</span>))]</span><br><span class="line"><span class="type">Return</span> an <span class="type">RDD</span> containing all pairs of elements <span class="keyword">with</span> matching keys in <span class="keyword">this</span> and other.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data1 = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">2</span>)))</span><br><span class="line"><span class="keyword">val</span> data2 = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">3</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">4</span>)))</span><br><span class="line">data1.join(data2).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data1: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">data2: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">1</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[(<span class="type">String</span>, (<span class="type">Int</span>, <span class="type">Int</span>))] = <span class="type">Array</span>((a,(<span class="number">1</span>,<span class="number">3</span>)), (a,(<span class="number">1</span>,<span class="number">4</span>)), (a,(<span class="number">2</span>,<span class="number">3</span>)), (a,(<span class="number">2</span>,<span class="number">4</span>)))</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="leftOuterJoin-和-rightOuterJoin-算子"><a href="#leftOuterJoin-和-rightOuterJoin-算子" class="headerlink" title="leftOuterJoin 和 rightOuterJoin 算子"></a>leftOuterJoin 和 rightOuterJoin 算子</h4><ul><li><p><strong>说明：</strong>leftOuterJoin（左外连接）和 rightOuterJoin（右外连接）相当于在 join 的基础上先判断一侧的 RDD 元素是否为空，如果为空，则填充为空。如果不为空，则将数据进行连接运算，并返回结果。</p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leftOuterJoin</span></span>[<span class="type">W</span>](other: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W</span>)]): <span class="type">RDD</span>[(<span class="type">K</span>, (<span class="type">V</span>, <span class="type">Option</span>[<span class="type">W</span>]))]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leftOuterJoin</span></span>[<span class="type">W</span>](other: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W</span>)], numPartitions: <span class="type">Int</span>): <span class="type">RDD</span>[(<span class="type">K</span>, (<span class="type">V</span>, <span class="type">Option</span>[<span class="type">W</span>]))]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">leftOuterJoin</span></span>[<span class="type">W</span>](other: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W</span>)], partitioner: <span class="type">Partitioner</span>): <span class="type">RDD</span>[(<span class="type">K</span>, (<span class="type">V</span>, <span class="type">Option</span>[<span class="type">W</span>]))]</span><br><span class="line"><span class="type">Perform</span> a left outer join of <span class="keyword">this</span> and other. <span class="type">For</span> each element (k, v) in <span class="keyword">this</span>, the resulting <span class="type">RDD</span> will either contain all pairs (k, (v, <span class="type">Some</span>(w))) <span class="keyword">for</span> w in other, or the pair (k, (v, <span class="type">None</span>)) <span class="keyword">if</span> no elements in other have key k. <span class="type">Hash</span>-partitions the output using the existing partitioner/parallelism level.</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">rightOuterJoin</span></span>[<span class="type">W</span>](other: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W</span>)]): <span class="type">RDD</span>[(<span class="type">K</span>, (<span class="type">Option</span>[<span class="type">V</span>], <span class="type">W</span>))]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">rightOuterJoin</span></span>[<span class="type">W</span>](other: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W</span>)], numPartitions: <span class="type">Int</span>): <span class="type">RDD</span>[(<span class="type">K</span>, (<span class="type">Option</span>[<span class="type">V</span>], <span class="type">W</span>))]</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">rightOuterJoin</span></span>[<span class="type">W</span>](other: <span class="type">RDD</span>[(<span class="type">K</span>, <span class="type">W</span>)], partitioner: <span class="type">Partitioner</span>): <span class="type">RDD</span>[(<span class="type">K</span>, (<span class="type">Option</span>[<span class="type">V</span>], <span class="type">W</span>))]</span><br><span class="line"><span class="type">Perform</span> a right outer join of <span class="keyword">this</span> and other. <span class="type">For</span> each element (k, w) in other, the resulting <span class="type">RDD</span> will either contain all pairs (k, (<span class="type">Some</span>(v), w)) <span class="keyword">for</span> v in <span class="keyword">this</span>, or the pair (k, (<span class="type">None</span>, w)) <span class="keyword">if</span> no elements in <span class="keyword">this</span> have key k. <span class="type">Hash</span>-partitions the resulting <span class="type">RDD</span> using the existing partitioner/parallelism level.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data1 = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">2</span>)))</span><br><span class="line"><span class="keyword">val</span> data2 = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">3</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">4</span>)))</span><br><span class="line">data1.leftOuterJoin(data2).collect</span><br><span class="line">data2.leftOuterJoin(data1).collect</span><br><span class="line">data1.rightOuterJoin(data2).collect</span><br><span class="line">data2.rightOuterJoin(data1).collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data1: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">data2: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">1</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[(<span class="type">String</span>, (<span class="type">Int</span>, <span class="type">Option</span>[<span class="type">Int</span>]))] = <span class="type">Array</span>((a,(<span class="number">2</span>,<span class="type">Some</span>(<span class="number">3</span>))), (a,(<span class="number">1</span>,<span class="type">Some</span>(<span class="number">3</span>))))</span><br><span class="line">res1: <span class="type">Array</span>[(<span class="type">String</span>, (<span class="type">Int</span>, <span class="type">Option</span>[<span class="type">Int</span>]))] = <span class="type">Array</span>((b,(<span class="number">4</span>,<span class="type">None</span>)), (a,(<span class="number">3</span>,<span class="type">Some</span>(<span class="number">1</span>))), (a,(<span class="number">3</span>,<span class="type">Some</span>(<span class="number">2</span>))))</span><br><span class="line">res2: <span class="type">Array</span>[(<span class="type">String</span>, (<span class="type">Option</span>[<span class="type">Int</span>], <span class="type">Int</span>))] = <span class="type">Array</span>((b,(<span class="type">None</span>,<span class="number">4</span>)), (a,(<span class="type">Some</span>(<span class="number">1</span>),<span class="number">3</span>)), (a,(<span class="type">Some</span>(<span class="number">2</span>),<span class="number">3</span>)))</span><br><span class="line">res3: <span class="type">Array</span>[(<span class="type">String</span>, (<span class="type">Option</span>[<span class="type">Int</span>], <span class="type">Int</span>))] = <span class="type">Array</span>((a,(<span class="type">Some</span>(<span class="number">3</span>),<span class="number">2</span>)), (a,(<span class="type">Some</span>(<span class="number">3</span>),<span class="number">1</span>)))</span><br></pre></td></tr></table></figure></p></li></ul><h2 id="Action-算子"><a href="#Action-算子" class="headerlink" title="Action 算子"></a>Action 算子</h2><p>本质上在 Action 算子中通过 SparkContext 进行了提交作业的 runJob 操作，触发了 RDD DAG 的执行。</p><h3 id="无输出"><a href="#无输出" class="headerlink" title="无输出"></a>无输出</h3><h4 id="foreach-算子"><a href="#foreach-算子" class="headerlink" title="foreach 算子"></a>foreach 算子</h4><ul><li><p><strong>说明：</strong>foreach 对 RDD 中的每个元素都应用 f 函数操作，不返回 RDD 和 Array， 而是返回 Uint。下图表示 foreach 算子通过用户自定义函数对每个数据项进行操作。本例中自定义函数为 println()，控制台打印所有数据项。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/foreach.png" alt="foreach 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">foreach</span></span>[<span class="type">U</span>](f: (<span class="type">T</span>) ⇒ <span class="type">U</span>)(<span class="keyword">implicit</span> executor: <span class="type">ExecutionContext</span>): <span class="type">Unit</span></span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">5</span>)</span><br><span class="line"><span class="keyword">var</span> sum = sc.accumulator(<span class="number">0</span>)</span><br><span class="line">data.foreach(sum += _)</span><br><span class="line">sum.value</span><br><span class="line">data.collect().foreach(println)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">sum: org.apache.spark.<span class="type">Accumulator</span>[<span class="type">Int</span>] = <span class="number">0</span></span><br><span class="line">res0: <span class="type">Int</span> = <span class="number">15</span></span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">2</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="number">4</span></span><br><span class="line"><span class="number">5</span></span><br></pre></td></tr></table></figure></p></li></ul><h3 id="HDFS"><a href="#HDFS" class="headerlink" title="HDFS"></a>HDFS</h3><h4 id="saveAsTextFile-算子"><a href="#saveAsTextFile-算子" class="headerlink" title="saveAsTextFile 算子"></a>saveAsTextFile 算子</h4><ul><li><p><strong>说明：</strong>使用元素的字符串表示形式将此 RDD 保存为文本文件。将数据输出，存储到 HDFS 的指定目录。</p><p>下面为 saveAsTextFile 函数的内部实现，其内部通过调用 saveAsHadoopFile 进行实现：</p><p><code>this.map(x =&gt; (NullWritable.get(), new Text(x.toString))).saveAsHadoopFile[TextOutputFormat[NullWritable, Text]](path)</code></p><p>将 RDD 中的每个元素映射转变为 (null, x.toString)，然后再将其写入 HDFS。下图中左侧方框代表 RDD 分区，右侧方框代表 HDFS 的 Block。通过函数将 RDD 的每个分区存储为 HDFS 中的一个 Block。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/save_as_text_file.png" alt="saveAsTextFile 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">saveAsTextFile</span></span>(path: <span class="type">String</span>): <span class="type">Unit</span></span><br><span class="line"><span class="type">Save</span> <span class="keyword">this</span> <span class="type">RDD</span> as a text file, using string representations of elements.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.partitions.size</span><br><span class="line">data.saveAsTextFile(<span class="string">&quot;/test/rdd&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[scala.collection.immutable.<span class="type">Range</span>.<span class="type">Inclusive</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Int</span> = <span class="number">3</span></span><br><span class="line"></span><br><span class="line">[root<span class="meta">@master</span>]# hdfs dfs -ls /test/rdd</span><br><span class="line"><span class="type">Found</span> <span class="number">4</span> items</span><br><span class="line">-rw-r--r--   <span class="number">3</span> root supergroup          <span class="number">0</span> <span class="number">2021</span><span class="number">-05</span><span class="number">-07</span> <span class="number">16</span>:<span class="number">57</span> /test/rdd/_SUCCESS</span><br><span class="line">-rw-r--r--   <span class="number">3</span> root supergroup          <span class="number">0</span> <span class="number">2021</span><span class="number">-05</span><span class="number">-07</span> <span class="number">16</span>:<span class="number">57</span> /test/rdd/part<span class="number">-00000</span></span><br><span class="line">-rw-r--r--   <span class="number">3</span> root supergroup          <span class="number">0</span> <span class="number">2021</span><span class="number">-05</span><span class="number">-07</span> <span class="number">16</span>:<span class="number">57</span> /test/rdd/part<span class="number">-00001</span></span><br><span class="line">-rw-r--r--   <span class="number">3</span> root supergroup          <span class="number">0</span> <span class="number">2021</span><span class="number">-05</span><span class="number">-07</span> <span class="number">16</span>:<span class="number">57</span> /test/rdd/part<span class="number">-00002</span></span><br></pre></td></tr></table></figure></p></li></ul><h4 id="saveAsObjectFile-算子"><a href="#saveAsObjectFile-算子" class="headerlink" title="saveAsObjectFile 算子"></a>saveAsObjectFile 算子</h4><ul><li><p><strong>说明：</strong>将此 RDD 保存为序列化对象的 SequenceFile。</p><p>saveAsObjectFile 将分区中的每 10 个元素组成一个 Array，然后将这个 Array 序列化，映射为 (Null, BytesWritable(Y)) 的元素，写入 HDFS 为 SequenceFile 的格式。下面代码为函数内部实现。</p><p><code>map(x=&gt;(NullWritable.get()，new BytesWritable(Utils.serialize(x))))</code></p><p>下图中的左侧方框代表 RDD 分区，右侧方框代表 HDFS 的 Block。通过函数将 RDD 的每个分区存储为 HDFS 上的一个 Block。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/save_as_object_file.png" alt="saveAsObjectFile 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">saveAsObjectFile</span></span>(path: <span class="type">String</span>): <span class="type">Unit</span></span><br><span class="line"><span class="type">Save</span> <span class="keyword">this</span> <span class="type">RDD</span> as a <span class="type">SequenceFile</span> of serialized objects.</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">saveAsTextFile</span></span>(path: <span class="type">String</span>, codec: <span class="type">Class</span>[_ &lt;: <span class="type">CompressionCodec</span>]): <span class="type">Unit</span></span><br><span class="line"><span class="type">Save</span> <span class="keyword">this</span> <span class="type">RDD</span> as a compressed text file, using string representations of elements.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.partitions.size</span><br><span class="line">data.saveAsObjectFile(<span class="string">&quot;/test/rdd&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[scala.collection.immutable.<span class="type">Range</span>.<span class="type">Inclusive</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Int</span> = <span class="number">3</span></span><br><span class="line"></span><br><span class="line">[root<span class="meta">@master</span>]# hdfs dfs -ls /test/rdd</span><br><span class="line"><span class="type">Found</span> <span class="number">4</span> items</span><br><span class="line">-rw-r--r--   <span class="number">3</span> root supergroup          <span class="number">0</span> <span class="number">2021</span><span class="number">-05</span><span class="number">-07</span> <span class="number">16</span>:<span class="number">57</span> /test/rdd/_SUCCESS</span><br><span class="line">-rw-r--r--   <span class="number">3</span> root supergroup          <span class="number">0</span> <span class="number">2021</span><span class="number">-05</span><span class="number">-07</span> <span class="number">16</span>:<span class="number">57</span> /test/rdd/part<span class="number">-00000</span></span><br><span class="line">-rw-r--r--   <span class="number">3</span> root supergroup          <span class="number">0</span> <span class="number">2021</span><span class="number">-05</span><span class="number">-07</span> <span class="number">16</span>:<span class="number">57</span> /test/rdd/part<span class="number">-00001</span></span><br><span class="line">-rw-r--r--   <span class="number">3</span> root supergroup          <span class="number">0</span> <span class="number">2021</span><span class="number">-05</span><span class="number">-07</span> <span class="number">16</span>:<span class="number">57</span> /test/rdd/part<span class="number">-00002</span></span><br><span class="line"></span><br><span class="line">[root<span class="meta">@master</span>]# hadoop fs -cat /test/rdd/part<span class="number">-00000</span></span><br><span class="line"><span class="type">SEQ</span> !org.apache.hadoop.io.<span class="type">NullWritable</span><span class="string">&quot;org.apache.hadoop.io.BytesWritableT</span></span><br></pre></td></tr></table></figure></p></li></ul><h3 id="Scala-集合和数据类型"><a href="#Scala-集合和数据类型" class="headerlink" title="Scala 集合和数据类型"></a>Scala 集合和数据类型</h3><h4 id="collect-算子"><a href="#collect-算子" class="headerlink" title="collect 算子"></a>collect 算子</h4><ul><li><p><strong>说明：</strong>返回一个包含此 RDD 中所有元素的数组。</p><p>collect 相当于 toArray，toArray 已经过时不推荐使用，collect 将分布式的 RDD 返回为一个单机的 scala Array 数组。在这个数组上运用 scala 的函数式操作。下图中左侧方框代表 RDD 分区，右侧方框代表单机内存中的数组。通过函数操作，将结果返回到 Driver 程序所在的节点，以数组形式存储。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/collect.png" alt="collect 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">collect</span></span>(): <span class="type">List</span>[<span class="type">T</span>]</span><br><span class="line"><span class="type">Return</span> an array that contains all of the elements in <span class="keyword">this</span> <span class="type">RDD</span>.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.collect</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="collectAsMap-算子"><a href="#collectAsMap-算子" class="headerlink" title="collectAsMap 算子"></a>collectAsMap 算子</h4><ul><li><p><strong>说明：</strong>collectAsMap 对 (K，V) 型的 RDD 数据返回一个单机 HashMap。对于重复 K 的 RDD 元素，后面的元素覆盖前面的元素。</p><p>下图中的左侧方框代表 RDD 分区，右侧方框代表单机数组。数据通过 collectAsMap 函数返回给 Driver 程序计算结果，结果以 HashMap 形式存储。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/collect_as_map.png" alt="collectAsMap 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">collectAsMap</span></span>(): <span class="type">Map</span>[<span class="type">K</span>, <span class="type">V</span>]</span><br><span class="line"><span class="type">Return</span> the key-value pairs in <span class="keyword">this</span> <span class="type">RDD</span> to the master as a <span class="type">Map</span>.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;b,2&quot;</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">3</span>)))</span><br><span class="line">data.collectAsMap</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: scala.collection.<span class="type">Map</span>[<span class="type">String</span>,<span class="type">Int</span>] = <span class="type">Map</span>(a -&gt; <span class="number">3</span>, b -&gt; <span class="number">2</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="reduceByKeyLocally-算子"><a href="#reduceByKeyLocally-算子" class="headerlink" title="reduceByKeyLocally 算子"></a>reduceByKeyLocally 算子</h4><ul><li><p><strong>说明：</strong>使用关联和可交换的 reduce 函数合并每个键的值，但是将结果作为 Map 返回。</p><p>实现的是先 reduce 再 collectAsMap 的功能，先对 RDD 的整体进行 reduce 操作，然后再收集所有结果返回为一个 HashMap。</p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">reduceByKeyLocally</span></span>(func: <span class="type">Function2</span>[<span class="type">V</span>, <span class="type">V</span>, <span class="type">V</span>]): <span class="type">Map</span>[<span class="type">K</span>, <span class="type">V</span>]</span><br><span class="line"><span class="type">Merge</span> the values <span class="keyword">for</span> each key using an associative and commutative reduce function, but <span class="keyword">return</span> the result immediately to the master as a <span class="type">Map</span>.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> data = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">0</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;c&quot;</span>, <span class="number">1</span>)))</span><br><span class="line">data.reduceByKeyLocally((x, y) =&gt; x + y)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: scala.collection.<span class="type">Map</span>[<span class="type">String</span>,<span class="type">Int</span>] = <span class="type">Map</span>(a -&gt; <span class="number">2</span>, b -&gt; <span class="number">3</span>, c -&gt; <span class="number">1</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="lookup-算子"><a href="#lookup-算子" class="headerlink" title="lookup 算子"></a>lookup 算子</h4><ul><li><p><strong>说明：</strong>返回 RDD 中 key 的值列表。lookup 函数对 (Key, Value) 型的 RDD 操作，返回指定 Key 对应的元素形成的 Seq。这个函数处理优化的部分在于，如果这个 RDD 包含分区器，则只会对应处理 K 所在的分区，然后返回由 (K,V) 形成的 Seq。如果 RDD 不包含分区器，则需要对全 RDD 元素进行暴力扫描处理，搜索指定 K 对应的元素。</p><p>下图中的左侧方框代表 RDD 分区，右侧方框代表 Seq，最后结果返回到 Driver 所在节点的应用中。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/lookup.png" alt="lookup 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">lookup</span></span>(key: <span class="type">K</span>): <span class="type">List</span>[<span class="type">V</span>]</span><br><span class="line"><span class="type">Return</span> the list of values in the <span class="type">RDD</span> <span class="keyword">for</span> key.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> data = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">0</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">1</span>)))</span><br><span class="line">data.lookup(<span class="string">&quot;a&quot;</span>)</span><br><span class="line">data.lookup(<span class="string">&quot;b&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Seq</span>[<span class="type">Int</span>] = <span class="type">WrappedArray</span>(<span class="number">0</span>, <span class="number">2</span>)</span><br><span class="line">res1: <span class="type">Seq</span>[<span class="type">Int</span>] = <span class="type">WrappedArray</span>(<span class="number">1</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="count-算子"><a href="#count-算子" class="headerlink" title="count 算子"></a>count 算子</h4><ul><li><p><strong>说明：</strong> 返回整个 RDD 的元素个数。</p><p>下图中，返回数据的个数为 5。一个方块代表一个 RDD 分区。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/count.png" alt="count 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">count</span></span>(): <span class="type">Long</span></span><br><span class="line"><span class="type">Return</span> the number of elements in the <span class="type">RDD</span>.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">5</span>)</span><br><span class="line">data.count</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Long</span> = <span class="number">5</span></span><br></pre></td></tr></table></figure></p></li></ul><h4 id="top-算子"><a href="#top-算子" class="headerlink" title="top 算子"></a>top 算子</h4><ul><li><p><strong>说明：</strong>返回此 RDD 中的前 k 个（最大）元素，并保持顺序。</p><p>相近函数:</p><ul><li>top 返回最大的 k 个元素。</li><li>take 返回最小的 k 个元素。</li><li>takeOrdered 返回前 k 个（最小）元素，并且在返回的数组中保持元素的顺序。</li><li>first 相当于 top(1) 返回整个 RDD 中的前 k 个元素。</li></ul></li></ul><ul><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">top</span></span>(num: <span class="type">Int</span>): <span class="type">List</span>[(<span class="type">K</span>, <span class="type">V</span>)]</span><br><span class="line"><span class="type">Returns</span> the top k (largest) elements from <span class="keyword">this</span> <span class="type">RDD</span> using the natural ordering <span class="keyword">for</span> <span class="type">T</span> and maintains the order.</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">top</span></span>(num: <span class="type">Int</span>, comp: <span class="type">Comparator</span>[(<span class="type">K</span>, <span class="type">V</span>)]): <span class="type">List</span>[(<span class="type">K</span>, <span class="type">V</span>)]</span><br><span class="line"><span class="type">Returns</span> the top k (largest) elements from <span class="keyword">this</span> <span class="type">RDD</span> as defined by the specified <span class="type">Comparator</span>[<span class="type">T</span>] and maintains the order.</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">take</span></span>(num: <span class="type">Int</span>): <span class="type">List</span>[(<span class="type">K</span>, <span class="type">V</span>)]</span><br><span class="line"><span class="type">Take</span> the first num elements of the <span class="type">RDD</span>.</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">takeOrdered</span></span>(num: <span class="type">Int</span>): <span class="type">List</span>[(<span class="type">K</span>, <span class="type">V</span>)]</span><br><span class="line"><span class="type">Returns</span> the first k (smallest) elements from <span class="keyword">this</span> <span class="type">RDD</span> using the natural ordering <span class="keyword">for</span> <span class="type">T</span> <span class="keyword">while</span> maintain the order.</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">takeOrdered</span></span>(num: <span class="type">Int</span>, comp: <span class="type">Comparator</span>[(<span class="type">K</span>, <span class="type">V</span>)]): <span class="type">List</span>[(<span class="type">K</span>, <span class="type">V</span>)]</span><br><span class="line"><span class="type">Returns</span> the first k (smallest) elements from <span class="keyword">this</span> <span class="type">RDD</span> as defined by the specified <span class="type">Comparator</span>[<span class="type">T</span>] and maintains the order.</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">first</span></span>(): (<span class="type">K</span>, <span class="type">V</span>)</span><br><span class="line"><span class="type">Return</span> the first element in <span class="keyword">this</span> <span class="type">RDD</span>.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>)</span><br><span class="line">data.top(<span class="number">3</span>)</span><br><span class="line">data.take(<span class="number">3</span>)</span><br><span class="line">data.takeOrdered(<span class="number">3</span>)</span><br><span class="line">data.first</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">10</span>, <span class="number">9</span>, <span class="number">8</span>)</span><br><span class="line">res1: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line">res2: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line">res3: <span class="type">Int</span> = <span class="number">1</span></span><br></pre></td></tr></table></figure></p></li></ul><h4 id="reduce-算子"><a href="#reduce-算子" class="headerlink" title="reduce 算子"></a>reduce 算子</h4><ul><li><p><strong>说明：</strong>使用指定的可交换和关联的二进制运算符对 RDD 的元素进行运算。</p><p>reduce 函数相当于对 RDD 中的元素进行 reduceLeft 函数的操作。</p><p>reduceLeft 先对两个元素 (K, V) 进行 reduce 函数操作，然后将结果和迭代器取出的下一个元素 (K, V) 进行 reduce 函数操作，直到迭代器遍历完所有元素，得到最后结果。在 RDD 中，先对每个分区中的所有元素 (K, V) 的集合分别进行 reduceLeft。每个分区形成的结果相当于一个元素 (K, V)，再对这个结果集合进行 reduceLeft 操作。</p><p>下图中的方框代表一个 RDD 分区，通过用户自定函数 f 将数据进行 reduce 运算。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/reduce.png" alt="reduce 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">reduce</span></span>(f: <span class="type">Function2</span>[(<span class="type">K</span>, <span class="type">V</span>), (<span class="type">K</span>, <span class="type">V</span>), (<span class="type">K</span>, <span class="type">V</span>)]): (<span class="type">K</span>, <span class="type">V</span>)</span><br><span class="line"><span class="type">Reduces</span> the elements of <span class="keyword">this</span> <span class="type">RDD</span> using the specified commutative and associative binary operator.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> data = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;c&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;d&quot;</span>, <span class="number">2</span>)))</span><br><span class="line">data.reduce((x, y) =&gt; (x._1 + <span class="string">&quot;@&quot;</span> + y._1, x._2 + y._2))</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: (<span class="type">String</span>, <span class="type">Int</span>) = (c<span class="meta">@d</span><span class="meta">@a</span><span class="meta">@b</span>,<span class="number">6</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="fold-算子"><a href="#fold-算子" class="headerlink" title="fold 算子"></a>fold 算子</h4><ul><li><p><strong>说明：</strong>使用给定的关联函数和中性的“零值”，汇总每个分区的元素，然后汇总所有分区的结果。</p><p>fold 和 reduce 的原理相同，但是与 reduce 不同，相当于每个 reduce 时，迭代器取的第一个元素是 zeroValue。</p><p>下图中通过下面的用户自定义函数进行 fold 运算，图中的一个方框代表一个 RDD 分区。读者可以参照 reduce 函数理解。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/fold.png" alt="fold 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fold</span></span>(zeroValue: (<span class="type">K</span>, <span class="type">V</span>))(f: <span class="type">Function2</span>[(<span class="type">K</span>, <span class="type">V</span>), (<span class="type">K</span>, <span class="type">V</span>), (<span class="type">K</span>, <span class="type">V</span>)]): (<span class="type">K</span>, <span class="type">V</span>)</span><br><span class="line"><span class="type">Aggregate</span> the elements of each partition, and then the results <span class="keyword">for</span> all the partitions, using a given associative function and a neutral <span class="string">&quot;zero value&quot;</span>.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> data = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">2</span>)), <span class="number">2</span>)</span><br><span class="line">data.fold((<span class="string">&quot;A&quot;</span>, <span class="number">10</span>))((x, y) =&gt; (x._1 + <span class="string">&quot;@&quot;</span> + y._1, x._2 + y._2))</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: (<span class="type">String</span>, <span class="type">Int</span>) = (<span class="type">A</span><span class="meta">@A</span><span class="meta">@a</span><span class="meta">@b</span><span class="meta">@A</span><span class="meta">@c</span><span class="meta">@d</span>,<span class="number">36</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="aggregate-算子"><a href="#aggregate-算子" class="headerlink" title="aggregate 算子"></a>aggregate 算子</h4><ul><li><p><strong>说明：</strong>使用给定的合并功能和中性的“零值”，汇总每个分区的元素，然后汇总所有分区的结果。此函数可以返回与该 RDD 的类型 T 不同的结果类型 U。因此，我们需要一个将 T 合并为 U 的操作，以及一个将两个 U 合并的操作。</p><p>aggregate 先对每个分区的所有元素进行 aggregate 操作，再对分区的结果进行 fold 操作。</p><p>aggregate 与 fold 和 reduce 的不同之处在于，aggregate 相当于采用归并的方式进行数据聚合，这种聚合是并行化的。而在 fold 和 reduce 函数的运算过程中，每个分区中需要进行串行处理，每个分区串行计算完结果，结果再按之前的方式进行聚合，并返回最终聚合结果。</p><p>下图图通过用户自定义函数对 RDD 进行 aggregate 的聚合操作，图中的每个方框代表一个 RDD 分区。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/aggregate.png" alt="aggregate 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">aggregate</span></span>[<span class="type">U</span>](zeroValue: <span class="type">U</span>)(seqOp: <span class="type">Function2</span>[<span class="type">U</span>, (<span class="type">K</span>, <span class="type">V</span>), <span class="type">U</span>], combOp: <span class="type">Function2</span>[<span class="type">U</span>, <span class="type">U</span>, <span class="type">U</span>]): <span class="type">U</span></span><br><span class="line"><span class="type">Aggregate</span> the elements of each partition, and then the results <span class="keyword">for</span> all the partitions, using given combine functions and a neutral <span class="string">&quot;zero value&quot;</span>.</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> data = sc.parallelize(<span class="type">Array</span>((<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">2</span>)), <span class="number">2</span>)</span><br><span class="line">data.aggregate((<span class="string">&quot;A&quot;</span>, <span class="number">10</span>))((x, y) =&gt; (x._1 + <span class="string">&quot;@&quot;</span> + y._1, x._2 + y._2), (x, y) =&gt; (x._1 + <span class="string">&quot;@&quot;</span> + y._1, x._2 + y._2))</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[(<span class="type">String</span>, <span class="type">Int</span>)] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: (<span class="type">String</span>, <span class="type">Int</span>) = (<span class="type">A</span><span class="meta">@A</span><span class="meta">@a</span><span class="meta">@b</span><span class="meta">@A</span><span class="meta">@c</span><span class="meta">@d</span>,<span class="number">36</span>)</span><br></pre></td></tr></table></figure></p></li></ul><h4 id="takeSample-算子"><a href="#takeSample-算子" class="headerlink" title="takeSample 算子"></a>takeSample 算子</h4><ul><li><p><strong>说明：</strong>在数组中返回此 RDD 的固定大小的采样子集</p><p>takeSample 函数和上面的 sample 函数是一个原理，但是不使用相对比例采样，而是按设定的采样个数进行采样，同时返回结果不再是 RDD，而是相当于对采样后的数据进行 collect，返回结果的集合为单机的数组。图中左侧的方框代表分布式的各个节点上的分区，右侧方框代表单机上返回的结果数组。通过 takeSample 对数据采样，设置为采样一份数据，返回结果为 V1。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/spark_rdd/take_sample.png" alt="takeSample 算子"></p></li><li><p><strong>用法：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">takeSample</span></span>(withReplacement: <span class="type">Boolean</span>, num: <span class="type">Int</span>, seed: <span class="type">Long</span> = <span class="type">Utils</span>.random.nextLong): <span class="type">Array</span>[<span class="type">T</span>]</span><br><span class="line"><span class="type">Return</span> a fixed-size sampled subset of <span class="keyword">this</span> <span class="type">RDD</span> in an array</span><br><span class="line"></span><br><span class="line">withReplacement</span><br><span class="line">whether sampling is done <span class="keyword">with</span> replacement</span><br><span class="line">是否通过更换进行采样</span><br><span class="line"></span><br><span class="line">num</span><br><span class="line">size of the returned sample</span><br><span class="line">返回样本的大小</span><br><span class="line"></span><br><span class="line">seed</span><br><span class="line">seed <span class="keyword">for</span> the random number generator</span><br><span class="line">随机数生成器的种子</span><br></pre></td></tr></table></figure></p></li><li><p><strong>示例：</strong></p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> data = sc.parallelize(<span class="number">1</span> to <span class="number">10</span>, <span class="number">3</span>)</span><br><span class="line">data.takeSample(<span class="literal">true</span>, <span class="number">1</span>, <span class="number">9</span>)</span><br><span class="line">data.takeSample(<span class="literal">false</span>, <span class="number">1</span>, <span class="number">9</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">data: org.apache.spark.rdd.<span class="type">RDD</span>[<span class="type">Int</span>] = <span class="type">ParallelCollectionRDD</span>[<span class="number">0</span>] at parallelize at &lt;console&gt;:<span class="number">25</span></span><br><span class="line">res0: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">1</span>)</span><br><span class="line">res1: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">3</span>)</span><br></pre></td></tr></table></figure></p></li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Spark/">Spark</category>
      
      
      <comments>https://blog.eurkon.com/post/c55e5115.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Linux 常用命令</title>
      <link>https://blog.eurkon.com/post/817c7d82.html</link>
      <guid>https://blog.eurkon.com/post/817c7d82.html</guid>
      <pubDate>Mon, 19 Apr 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;系统信息&quot;&gt;&lt;a href=&quot;#系统信息&quot; class=&quot;headerlink&quot; title=&quot;系统信息&quot;&gt;&lt;/a&gt;系统信息&lt;/h2&gt;&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;t</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="系统信息"><a href="#系统信息" class="headerlink" title="系统信息"></a>系统信息</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>arch</td><td>显示机器的处理器架构</td></tr><tr><td>uname -m</td><td>显示机器的处理器架构</td></tr><tr><td>uname -r</td><td>显示正在使用的内核版本</td></tr><tr><td>dmidecode -q</td><td>显示硬件系统部件 -（SMBIOS/DMI）</td></tr><tr><td>hdparm -i /dev/hda</td><td>罗列一个磁盘的架构特性</td></tr><tr><td>hdparm -tT /dev/sda</td><td>在磁盘上执行测试性读取操作</td></tr><tr><td>cat /proc/cpuinfo</td><td>显示 CPU info 的信息</td></tr><tr><td>cat /proc/interrupts</td><td>显示中断</td></tr><tr><td>cat /proc/meminfo</td><td>校验内存使用</td></tr><tr><td>cat /proc/swaps</td><td>显示哪些 swap 被使用</td></tr><tr><td>cat /proc/version</td><td>显示内核的版本</td></tr><tr><td>cat /proc/net/dev</td><td>显示网络适配器及统计</td></tr><tr><td>cat /proc/mounts</td><td>显示已加载的文件系统</td></tr><tr><td>lspci -tv</td><td>罗列 PCI 设备</td></tr><tr><td>lsusb -tv</td><td>显示 USB 设备</td></tr><tr><td>date</td><td>显示系统日期</td></tr><tr><td>cal 2007</td><td>显示 2007 年的日历表</td></tr><tr><td>date 041217002007.00</td><td>设置日期和时间 - 月日时分年.秒</td></tr><tr><td>clock -w</td><td>将时间修改保存到 BIOS</td></tr></tbody></table></div><h2 id="系统关机、重启以及登出"><a href="#系统关机、重启以及登出" class="headerlink" title="系统关机、重启以及登出"></a>系统关机、重启以及登出</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>shutdown -h now</td><td>关闭系统</td></tr><tr><td>init 0</td><td>关闭系统</td></tr><tr><td>telinit 0</td><td>关闭系统</td></tr><tr><td>shutdown -h hours:minutes &amp;</td><td>按预定时间关闭系统</td></tr><tr><td>shutdown -c</td><td>取消按预定时间关闭系统</td></tr><tr><td>shutdown -r now</td><td>重启</td></tr><tr><td>reboot</td><td>重启</td></tr><tr><td>logout</td><td>注销</td></tr></tbody></table></div><h2 id="文件和目录"><a href="#文件和目录" class="headerlink" title="文件和目录"></a>文件和目录</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>cd /home</td><td>进入 &#39;/home&#39; 目录&#39;</td></tr><tr><td>cd ..</td><td>返回上一级目录</td></tr><tr><td>cd ../..</td><td>返回上两级目录</td></tr><tr><td>cd</td><td>进入个人的主目录</td></tr><tr><td>cd ~user1</td><td>进入个人的主目录</td></tr><tr><td>cd -</td><td>返回上次所在的目录</td></tr><tr><td>pwd</td><td>显示工作路径</td></tr><tr><td>ls</td><td>查看目录中的文件</td></tr><tr><td>ls -F</td><td>查看目录中的文件</td></tr><tr><td>ls -l</td><td>显示文件和目录的详细资料</td></tr><tr><td>ls -a</td><td>显示隐藏文件</td></tr><tr><td>ls <em>[0-9]</em></td><td>显示包含数字的文件名和目录名</td></tr><tr><td>tree</td><td>显示文件和目录由根目录开始的树形结构</td></tr><tr><td>lstree</td><td>显示文件和目录由根目录开始的树形结构</td></tr><tr><td>mkdir dir1</td><td>创建一个叫做 &#39;dir1&#39; 的目录</td></tr><tr><td>mkdir dir1</td><td>dir2 同时创建两个目录</td></tr><tr><td>mkdir -p /tmp/dir1/dir2</td><td>创建一个目录树</td></tr><tr><td>rm -f file1</td><td>删除一个叫做 &#39;file1&#39; 的文件</td></tr><tr><td>rmdir dir1</td><td>删除一个叫做 &#39;dir1&#39; 的目录</td></tr><tr><td>rm -rf dir1</td><td>删除一个叫做 &#39;dir1&#39; 的目录并同时删除其内容</td></tr><tr><td>rm -rf dir1 dir2</td><td>同时删除两个目录及它们的内容</td></tr><tr><td>mv dir1 new_dir</td><td>重命名/移动 一个目录</td></tr><tr><td>cp file1 file2</td><td>复制一个文件</td></tr><tr><td>cp dir/* .</td><td>复制一个目录下的所有文件到当前工作目录</td></tr><tr><td>cp -a /tmp/dir1 .</td><td>复制一个目录到当前工作目录</td></tr><tr><td>cp -a dir1 dir2</td><td>复制一个目录</td></tr><tr><td>ln -s file1 lnk1</td><td>创建一个指向文件或目录的软链接</td></tr><tr><td>ln file1 lnk1</td><td>创建一个指向文件或目录的物理链接</td></tr><tr><td>touch -t 0712250000 file1</td><td>修改一个文件或目录的时间戳 - YYMMDDhhmm</td></tr><tr><td>file file1</td><td>将文件的类型输出为文本</td></tr><tr><td>iconv -l</td><td>列出已知的编码</td></tr><tr><td>iconv -f fromEncoding -t toEncoding inputFile &gt; outputFile</td><td>转换文件编码类型并创建新文件</td></tr><tr><td>find . -maxdepth 1 -name *.jpg -print -exec convert &quot;{}&quot; -resize 80x60 &quot;thumbs/{}&quot;</td><td>批量调整 &#39;.jpg&#39; 结尾的文件尺寸大小</td></tr></tbody></table></div><h2 id="文件搜索"><a href="#文件搜索" class="headerlink" title="文件搜索"></a>文件搜索</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>find / -name file1</td><td>从 &#39;/&#39; 开始进入根文件系统搜索文件和目录</td></tr><tr><td>find / -user user1</td><td>搜索属于用户 &#39;user1&#39; 的文件和目录</td></tr><tr><td>find /home/user1 -name *.bin</td><td>在目录 &#39;/home/user1&#39; 中搜索带有&#39;.bin&#39; 结尾的文件</td></tr><tr><td>find /usr/bin -type f -atime +100</td><td>搜索在过去 100 天内未被使用过的执行文件</td></tr><tr><td>find /usr/bin -type f -mtime -10</td><td>搜索在 10 天内被创建或者修改过的文件</td></tr><tr><td>find / -name *.rpm -exec chmod 755 &#39;{}&#39;</td><td>搜索以 &#39;.rpm&#39; 结尾的文件并定义其权限</td></tr><tr><td>find / -xdev -name *.rpm</td><td>搜索以 &#39;.rpm&#39; 结尾的文件，忽略光驱、捷盘等可移动设备</td></tr><tr><td>locate *.ps</td><td>寻找以 &#39;.ps&#39; 结尾的文件 - 先运行 &#39;updatedb&#39; 命令</td></tr><tr><td>whereis halt</td><td>显示一个二进制文件、源码或可执行文件的位置</td></tr><tr><td>which halt</td><td>显示一个二进制文件或可执行文件的完整路径</td></tr></tbody></table></div><h2 id="挂载一个文件系统"><a href="#挂载一个文件系统" class="headerlink" title="挂载一个文件系统"></a>挂载一个文件系统</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>mount /dev/hda2 /mnt/hda2</td><td>挂载一个叫做 hda2 的盘 - 确定目录 &#39;/mnt/hda2&#39; 已经存在</td></tr><tr><td>umount /dev/hda2</td><td>卸载一个叫做 hda2 的盘 - 先从挂载点 &#39;/mnt/hda2&#39; 退出</td></tr><tr><td>fuser -km /mnt/hda2</td><td>当设备繁忙时强制卸载</td></tr><tr><td>umount -n /mnt/hda2</td><td>运行卸载操作而不写入 &#39;/etc/mtab&#39; 文件- 当文件为只读或当磁盘写满时非常有用</td></tr><tr><td>mount /dev/fd0 /mnt/floppy</td><td>挂载一个软盘</td></tr><tr><td>mount /dev/cdrom /mnt/cdrom</td><td>挂载一个 cdrom 或 dvdrom</td></tr><tr><td>mount /dev/hdc /mnt/cdrecorder</td><td>挂载一个 cdrw 或 dvdrom</td></tr><tr><td>mount /dev/hdb /mnt/cdrecorder</td><td>挂载一个 cdrw 或 dvdrom</td></tr><tr><td>mount -o loop file.iso /mnt/cdrom</td><td>挂载一个文件或 ISO 镜像文件</td></tr><tr><td>mount -t vfat /dev/hda5 /mnt/hda5</td><td>挂载一个 Windows FAT32 文件系统</td></tr><tr><td>mount /dev/sda1 /mnt/usbdisk</td><td>挂载一个 usb 捷盘或闪存设备</td></tr><tr><td>mount -t smbfs -o username=user,password=pass //WinClient/share /mnt/share</td><td>挂载一个 windows 网络共享</td></tr></tbody></table></div><h2 id="磁盘空间"><a href="#磁盘空间" class="headerlink" title="磁盘空间"></a>磁盘空间</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>df -h</td><td>显示已经挂载的分区列表</td></tr><tr><td>ls -lSr &vert; more</td><td>以尺寸大小排列文件和目录</td></tr><tr><td>du -sh dir1</td><td>估算目录 &#39;dir1&#39; 已经使用的磁盘空间&#39;</td></tr><tr><td>du -sk * &vert; sort -rn</td><td>以容量大小为依据依次显示文件和目录的大小</td></tr><tr><td>rpm -q -a --qf &#39;%10{SIZE}t%{NAME}n&#39; &vert; sort -k1,1n</td><td>以大小为依据依次显示已安装的 rpm 包所使用的空间（fedora，RedHat 类系统）</td></tr><tr><td>dpkg-query -W -f=&#39;${Installed-Size10}t${Package}n&#39; &vert; sort -k1,1n</td><td>以大小为依据显示已安装的 deb 包所使用的空间（ubuntu，debian 类系统）</td></tr></tbody></table></div><h2 id="用户和群组"><a href="#用户和群组" class="headerlink" title="用户和群组"></a>用户和群组</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>groupadd group_name</td><td>创建一个新用户组</td></tr><tr><td>groupdel group_name</td><td>删除一个用户组</td></tr><tr><td>groupmod -n new_group_name old_group_name</td><td>重命名一个用户组</td></tr><tr><td>useradd -c &quot;Name Surname&quot; -g admin -d /home/user1 -s /bin/bash user1</td><td>创建一个属于 &quot;admin&quot; 用户组的用户</td></tr><tr><td>useradd user1</td><td>创建一个新用户</td></tr><tr><td>userdel -r user1</td><td>删除一个用户（&#39;-r&#39; 排除主目录）</td></tr><tr><td>usermod -c &quot;User FTP&quot; -g system -d /ftp/user1 -s /bin/nologin user1</td><td>修改用户属性</td></tr><tr><td>passwd</td><td>修改密码</td></tr><tr><td>passwd user1</td><td>修改一个用户的密码（只允许 root 执行）</td></tr><tr><td>chage -E 2005-12-31 user1</td><td>设置用户密码的失效期限</td></tr><tr><td>pwck</td><td>检查 &#39;/etc/passwd&#39; 的文件格式和语法修正以及存在的用户</td></tr><tr><td>grpck</td><td>检查 &#39;/etc/passwd&#39; 的文件格式和语法修正以及存在的群组</td></tr><tr><td>newgrp group_name</td><td>登陆进一个新的群组以改变新创建文件的预设群组</td></tr></tbody></table></div><h2 id="文件的权限"><a href="#文件的权限" class="headerlink" title="文件的权限"></a>文件的权限</h2><p><strong>使用 &quot;+&quot; 设置权限，使用 &quot;-&quot; 用于取消</strong></p><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>ls -lh</td><td>显示权限</td></tr><tr><td>ls /tmp &vert; pr -T5 -W$COLUMNS</td><td>将终端划分成 5 栏显示</td></tr><tr><td>chmod ugo+rwx directory1</td><td>设置目录的所有人（u）、群组（g）以及其他人（o）以读（r）、写（w）和执行（x）的权限</td></tr><tr><td>chmod go-rwx directory1</td><td>删除群组（g）与其他人（o）对目录的读写执行权限</td></tr><tr><td>chown user1 file1</td><td>改变一个文件的所有人属性</td></tr><tr><td>chown -R user1 directory1</td><td>改变一个目录的所有人属性并同时改变改目录下所有文件的属性</td></tr><tr><td>chgrp group1 file1</td><td>改变文件的群组</td></tr><tr><td>chown user1:group1 file1</td><td>改变一个文件的所有人和群组属性</td></tr><tr><td>find / -perm -u+s</td><td>罗列一个系统中所有使用了 SUID 控制的文件</td></tr><tr><td>chmod u+s /bin/file1</td><td>设置一个二进制文件的 SUID 位 - 运行该文件的用户也被赋予和所有者同样的权限</td></tr><tr><td>chmod u-s /bin/file1</td><td>禁用一个二进制文件的 SUID 位</td></tr><tr><td>chmod g+s /home/public</td><td>设置一个目录的 SGID 位 - 类似 SUID，不过这是针对目录的</td></tr><tr><td>chmod g-s /home/public</td><td>禁用一个目录的 SGID 位</td></tr><tr><td>chmod o+t /home/public</td><td>设置一个文件的 STIKY 位 - 只允许合法所有人删除文件</td></tr><tr><td>chmod o-t /home/public</td><td>禁用一个目录的 STIKY 位</td></tr></tbody></table></div><h2 id="文件的特殊属性"><a href="#文件的特殊属性" class="headerlink" title="文件的特殊属性"></a>文件的特殊属性</h2><p><strong>使用 &quot;+&quot; 设置权限，使用 &quot;-&quot; 用于取消</strong></p><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>chattr +a file1</td><td>只允许以追加方式读写文件</td></tr><tr><td>chattr +c file1</td><td>允许这个文件能被内核自动压缩/解压</td></tr><tr><td>chattr +d file1</td><td>在进行文件系统备份时，dump 程序将忽略这个文件</td></tr><tr><td>chattr +i file1</td><td>设置成不可变的文件，不能被删除、修改、重命名或者链接</td></tr><tr><td>chattr +s file1</td><td>允许一个文件被安全地删除</td></tr><tr><td>chattr +S file1</td><td>一旦应用程序对这个文件执行了写操作，使系统立刻把修改的结果写到磁盘</td></tr><tr><td>chattr +u file1</td><td>若文件被删除，系统会允许你在以后恢复这个被删除的文件</td></tr><tr><td>lsattr</td><td>显示特殊的属性</td></tr></tbody></table></div><h2 id="打包和解压缩文件"><a href="#打包和解压缩文件" class="headerlink" title="打包和解压缩文件"></a>打包和解压缩文件</h2><h3 id="解压缩"><a href="#解压缩" class="headerlink" title="解压缩"></a>解压缩</h3><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>bunzip2 file1.bz2</td><td>解压一个叫做 &#39;file1.bz2&#39; 的文件</td></tr><tr><td>bzip2 file1</td><td>压缩一个叫做 &#39;file1&#39; 的文件</td></tr><tr><td>gunzip file1.gz</td><td>解压一个叫做 &#39;file1.gz&#39; 的文件</td></tr><tr><td>gzip file1</td><td>压缩一个叫做 &#39;file1 &#39;的文件</td></tr><tr><td>gzip -9 file1</td><td>最大程度压缩</td></tr><tr><td>rar a file1.rar test_file</td><td>创建一个叫做 &#39;file1.rar&#39; 的包</td></tr><tr><td>rar a file1.rar file1 file2 dir1</td><td>同时压缩 &#39;file1&#39;, &#39;file2&#39; 以及目录 &#39;dir1&#39;</td></tr><tr><td>unrar x file1.rar</td><td>解压 rar 包</td></tr><tr><td>tar -cvf archive.tar file1</td><td>创建一个非压缩的 tarball</td></tr><tr><td>tar -cvf archive.tar file1 file2 dir1</td><td>创建一个包含了 &#39;file1&#39;, &#39;file2&#39; 以及 &#39;dir1&#39; 的包</td></tr><tr><td>tar -tf archive.tar</td><td>显示一个包中的内容</td></tr><tr><td>tar -xvf archive.tar</td><td>释放一个包</td></tr><tr><td>tar -xvf archive.tar -C /tmp</td><td>将压缩包释放到 /tmp 目录下</td></tr><tr><td>tar -cvfj archive.tar.bz2 dir1</td><td>创建一个 bzip2 格式的压缩包</td></tr><tr><td>tar -jxvf archive.tar.bz2</td><td>解压一个 bzip2 格式的压缩包</td></tr><tr><td>tar -cvfz archive.tar.gz dir1</td><td>创建一个 gzip 格式的压缩包</td></tr><tr><td>tar -zxvf archive.tar.gz</td><td>解压一个 gzip 格式的压缩包</td></tr><tr><td>zip file1.zip file1</td><td>创建一个 zip 格式的压缩包</td></tr><tr><td>zip -r file1.zip file1 file2 dir1</td><td>将几个文件和目录同时压缩成一个 zip 格式的压缩包</td></tr><tr><td>unzip file1.zip</td><td>解压一个 zip 格式压缩包</td></tr></tbody></table></div><h3 id="RPM-包"><a href="#RPM-包" class="headerlink" title="RPM 包"></a>RPM 包</h3><p><strong>Fedora，RedHat 及类似系统</strong></p><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>rpm -ivh package.rpm</td><td>安装一个 rpm 包</td></tr><tr><td>rpm -ivh --nodeeps package.rpm</td><td>安装一个 rpm 包而忽略依赖关系警告</td></tr><tr><td>rpm -U package.rpm</td><td>更新一个 rpm 包但不改变其配置文件</td></tr><tr><td>rpm -F package.rpm</td><td>更新一个确定已经安装的 rpm 包</td></tr><tr><td>rpm -e package_name.rpm</td><td>删除一个 rpm 包</td></tr><tr><td>rpm -qa</td><td>显示系统中所有已经安装的 rpm 包</td></tr><tr><td>rpm -qa &vert; grep httpd</td><td>显示所有名称中包含 &quot;httpd&quot; 字样的 rpm 包</td></tr><tr><td>rpm -qi package_name</td><td>获取一个已安装包的特殊信息</td></tr><tr><td>rpm -qg &quot;System Environment/Daemons&quot;</td><td>显示一个组件的 rpm 包</td></tr><tr><td>rpm -ql package_name</td><td>显示一个已经安装的 rpm 包提供的文件列表</td></tr><tr><td>rpm -qc package_name</td><td>显示一个已经安装的 rpm 包提供的配置文件列表</td></tr><tr><td>rpm -q package_name --whatrequires</td><td>显示与一个 rpm 包存在依赖关系的列表</td></tr><tr><td>rpm -q package_name --whatprovides</td><td>显示一个 rpm 包所占的体积</td></tr><tr><td>rpm -q package_name --scripts</td><td>显示在安装/删除期间所执行的脚本</td></tr><tr><td>rpm -q package_name --changelog</td><td>显示一个 rpm 包的修改历史</td></tr><tr><td>rpm -qf /etc/httpd/conf/httpd.conf</td><td>确认所给的文件由哪个 rpm 包所提供</td></tr><tr><td>rpm -qp package.rpm -l</td><td>显示由一个尚未安装的 rpm 包提供的文件列表</td></tr><tr><td>rpm --import /media/cdrom/RPM-GPG-KEY</td><td>导入公钥数字证书</td></tr><tr><td>rpm --checksig package.rpm</td><td>确认一个 rpm 包的完整性</td></tr><tr><td>rpm -qa gpg-pubkey</td><td>确认已安装的所有 rpm 包的完整性</td></tr><tr><td>rpm -V package_name</td><td>检查文件尺寸、许可、类型、所有者、群组、MD5 检查以及最后修改时间</td></tr><tr><td>rpm -Va</td><td>检查系统中所有已安装的 rpm 包- 小心使用</td></tr><tr><td>rpm -Vp package.rpm</td><td>确认一个 rpm 包还未安装</td></tr><tr><td>rpm2cpio package.rpm &vert; cpio --extract --make-directories <em>bin</em></td><td>从一个 rpm 包运行可执行文件</td></tr><tr><td>rpm -ivh /usr/src/RedHat/RPMS/arch/package.rpm</td><td>从一个 rpm 源码安装一个构建好的包</td></tr><tr><td>rpmbuild --rebuild package_name.src.rpm</td><td>从一个 rpm 源码构建一个 rpm 包</td></tr></tbody></table></div><h3 id="YUM-软件包升级器"><a href="#YUM-软件包升级器" class="headerlink" title="YUM 软件包升级器"></a>YUM 软件包升级器</h3><p><strong>Fedora，RedHat 及类似系统</strong></p><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>yum install package_name</td><td>下载并安装一个 rpm 包</td></tr><tr><td>yum localinstall package_name.rpm</td><td>将安装一个 rpm 包，使用你自己的软件仓库为你解决所有依赖关系</td></tr><tr><td>yum update package_name.rpm</td><td>更新当前系统中所有安装的 rpm 包</td></tr><tr><td>yum update package_name</td><td>更新一个 rpm 包</td></tr><tr><td>yum remove package_name</td><td>删除一个 rpm 包</td></tr><tr><td>yum list</td><td>列出当前系统中安装的所有包</td></tr><tr><td>yum search package_name</td><td>在 rpm 仓库中搜寻软件包</td></tr><tr><td>yum clean packages</td><td>清理 rpm 缓存删除下载的包</td></tr><tr><td>yum clean headers</td><td>删除所有头文件</td></tr><tr><td>yum clean all</td><td>删除所有缓存的包和头文件</td></tr></tbody></table></div><h3 id="DEB-包"><a href="#DEB-包" class="headerlink" title="DEB 包"></a>DEB 包</h3><p><strong>Debian，Ubuntu 以及类似系统</strong></p><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>dpkg -i package.deb</td><td>安装/更新一个 deb 包</td></tr><tr><td>dpkg -r package_name</td><td>从系统删除一个 deb 包</td></tr><tr><td>dpkg -l</td><td>显示系统中所有已经安装的 deb 包</td></tr><tr><td>dpkg -l &vert; grep httpd</td><td>显示所有名称中包含 &quot;httpd&quot; 字样的 deb 包</td></tr><tr><td>dpkg -s package_name</td><td>获得已经安装在系统中一个特殊包的信息</td></tr><tr><td>dpkg -L package_name</td><td>显示系统中已经安装的一个 deb 包所提供的文件列表</td></tr><tr><td>dpkg --contents package.deb</td><td>显示尚未安装的一个包所提供的文件列表</td></tr><tr><td>dpkg -S /bin/ping</td><td>确认所给的文件由哪个 deb 包提供</td></tr></tbody></table></div><h3 id="APT-软件工具（Debian-Ubuntu-以及类似系统）"><a href="#APT-软件工具（Debian-Ubuntu-以及类似系统）" class="headerlink" title="APT 软件工具（Debian, Ubuntu 以及类似系统）"></a>APT 软件工具（Debian, Ubuntu 以及类似系统）</h3><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>apt-get install package_name</td><td>安装/更新一个 deb 包</td></tr><tr><td>apt-cdrom install package_name</td><td>从光盘安装/更新一个 deb 包</td></tr><tr><td>apt-get update</td><td>升级列表中的软件包</td></tr><tr><td>apt-get upgrade</td><td>升级所有已安装的软件</td></tr><tr><td>apt-get remove package_name</td><td>从系统删除一个 deb 包</td></tr><tr><td>apt-get check</td><td>确认依赖的软件仓库正确</td></tr><tr><td>apt-get clean</td><td>从下载的软件包中清理缓存</td></tr><tr><td>apt-cache search searched-package</td><td>返回包含所要搜索字符串的软件包名称</td></tr></tbody></table></div><h2 id="查看文件内容"><a href="#查看文件内容" class="headerlink" title="查看文件内容"></a>查看文件内容</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>cat file1</td><td>从第一个字节开始正向查看文件的内容</td></tr><tr><td>tac file1</td><td>从最后一行开始反向查看一个文件的内容</td></tr><tr><td>more file1</td><td>查看一个长文件的内容</td></tr><tr><td>less file1</td><td>类似于 &#39;more&#39; 命令，但是它允许在文件中和正向操作一样的反向操作</td></tr><tr><td>head -2 file1</td><td>查看一个文件的前两行</td></tr><tr><td>tail -2 file1</td><td>查看一个文件的最后两行</td></tr><tr><td>tail -f /var/log/messages</td><td>实时查看被添加到一个文件中的内容</td></tr></tbody></table></div><h2 id="文本处理"><a href="#文本处理" class="headerlink" title="文本处理"></a>文本处理</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>cat file1 file2 ... &vert; command &lt;&gt; file1_in.txt_or_file1_out.txt</td><td>合并一个文件的详细说明文本，并将简介写出新文件中</td></tr><tr><td>cat file1 &vert; command( sed, grep, awk, grep, etc...) &gt; result.txt</td><td>合并一个文件的详细说明文本，并将简介写入一个新文件中</td></tr><tr><td>cat file1 &vert; command( sed, grep, awk, grep, etc...) &gt;&gt; result.txt</td><td>合并一个文件的详细说明文本，并将简介写入一个已有的文件中</td></tr><tr><td>grep Aug /var/log/messages</td><td>在文件 &#39;/var/log/messages&#39; 中查找关键词 &quot;Aug&quot;</td></tr><tr><td>grep ^Aug /var/log/messages</td><td>在文件 &#39;/var/log/messages&#39; 中查找以 &quot;Aug&quot; 开始的词汇</td></tr><tr><td>grep [0-9] /var/log/messages</td><td>选择 &#39;/var/log/messages&#39; 文件中所有包含数字的行</td></tr><tr><td>grep Aug -R /var/log/*</td><td>在目录 &#39;/var/log&#39; 及随后的目录中搜索字符串 &quot;Aug&quot;</td></tr><tr><td>sed &#39;s/stringa1/stringa2/g&#39; example.txt</td><td>将 example.txt 文件中的 &quot;string1&quot; 替换成 &quot;string2&quot;</td></tr><tr><td>sed &#39;/^$/d&#39; example.txt</td><td>从 example.txt 文件中删除所有空白行</td></tr><tr><td>sed &#39;/ *#/d /^$/d&#39; example.txt</td><td>从 example.txt 文件中删除所有注释和空白行</td></tr><tr><td>echo &#39;esempio&#39; &vert; tr &#39;[:lower:]&#39; &#39;[:upper:]&#39;</td><td>合并上下单元格内容</td></tr><tr><td>sed -e &#39;1d&#39; result.txt</td><td>从文件 example.txt 中排除第一行</td></tr><tr><td>sed -n &#39;/stringa1/p&#39;</td><td>查看只包含词汇 &quot;string1&quot; 的行</td></tr><tr><td>sed -e &#39;s/ *$//&#39; example.txt</td><td>删除每一行最后的空白字符</td></tr><tr><td>sed -e &#39;s/stringa1//g&#39; example.txt</td><td>从文档中只删除词汇 &quot;string1&quot; 并保留剩余全部</td></tr><tr><td>sed -n &#39;1,5p5q&#39; example.txt</td><td>查看从第一行到第 5 行内容</td></tr><tr><td>sed -n &#39;5p5q&#39; example.txt</td><td>查看第 5 行</td></tr><tr><td>sed -e &#39;s/00*/0/g&#39; example.txt</td><td>用单个零替换多个零</td></tr><tr><td>cat -n file1</td><td>标示文件的行数</td></tr><tr><td>cat example.txt &vert; awk &#39;NR%2==1&#39;</td><td>删除 example.txt 文件中的所有偶数行</td></tr><tr><td>echo a b c &vert; awk &#39;{print $1}&#39;</td><td>查看一行第一栏</td></tr><tr><td>echo a b c &vert; awk &#39;{print $1,$3}&#39;</td><td>查看一行的第一和第三栏</td></tr><tr><td>paste file1 file2</td><td>合并两个文件或两栏的内容</td></tr><tr><td>paste -d &#39;+&#39; file1 file2</td><td>合并两个文件或两栏的内容，中间用 &quot;+&quot; 区分</td></tr><tr><td>sort file1 file2</td><td>排序两个文件的内容</td></tr><tr><td>sort file1 file2 &vert; uniq</td><td>取出两个文件的并集（重复的行只保留一份）</td></tr><tr><td>sort file1 file2 &vert; uniq -u</td><td>删除交集，留下其他的行</td></tr><tr><td>sort file1 file2 &vert; uniq -d</td><td>取出两个文件的交集（只留下同时存在于两个文件中的文件）</td></tr><tr><td>comm -1 file1 file2</td><td>比较两个文件的内容只删除 &#39;file1&#39; 所包含的内容</td></tr><tr><td>comm -2 file1 file2</td><td>比较两个文件的内容只删除 &#39;file2&#39; 所包含的内容</td></tr><tr><td>comm -3 file1 file2</td><td>比较两个文件的内容只删除两个文件共有的部分</td></tr></tbody></table></div><h2 id="字符设置和文件格式转换"><a href="#字符设置和文件格式转换" class="headerlink" title="字符设置和文件格式转换"></a>字符设置和文件格式转换</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>dos2unix filedos.txt fileunix.txt</td><td>将一个文本文件的格式从 MSDOS 转换成 UNIX</td></tr><tr><td>unix2dos fileunix.txt filedos.txt</td><td>将一个文本文件的格式从 UNIX 转换成 MSDOS</td></tr><tr><td>recode ..HTML &lt; page.txt &gt; page.html</td><td>将一个文本文件转换成 html 文件</td></tr><tr><td>recode -l &vert; more</td><td>显示所有允许的转换格式</td></tr></tbody></table></div><h2 id="文件系统分析"><a href="#文件系统分析" class="headerlink" title="文件系统分析"></a>文件系统分析</h2><h3 id="检查-修复磁盘"><a href="#检查-修复磁盘" class="headerlink" title="检查/修复磁盘"></a>检查/修复磁盘</h3><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>badblocks -v /dev/hda1</td><td>检查磁盘 hda1 上的坏磁块</td></tr><tr><td>fsck /dev/hda1</td><td>修复/检查 hda1 磁盘上 linux 文件系统的完整性</td></tr><tr><td>fsck.ext2 /dev/hda1</td><td>修复/检查 hda1 磁盘上 ext2 文件系统的完整性</td></tr><tr><td>e2fsck /dev/hda1</td><td>修复/检查 hda1 磁盘上 ext2 文件系统的完整性</td></tr><tr><td>e2fsck -j /dev/hda1</td><td>修复/检查 hda1 磁盘上 ext3 文件系统的完整性</td></tr><tr><td>fsck.ext3 /dev/hda1</td><td>修复/检查 hda1 磁盘上 ext3 文件系统的完整性</td></tr><tr><td>fsck.vfat /dev/hda1</td><td>修复/检查 hda1 磁盘上 fat 文件系统的完整性</td></tr><tr><td>fsck.msdos /dev/hda1</td><td>修复/检查 hda1 磁盘上 dos 文件系统的完整性</td></tr><tr><td>dosfsck /dev/hda1</td><td>修复/检查 hda1 磁盘上 dos 文件系统的完整性</td></tr></tbody></table></div><h3 id="初始化一个文件系统"><a href="#初始化一个文件系统" class="headerlink" title="初始化一个文件系统"></a>初始化一个文件系统</h3><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>mkfs /dev/hda1</td><td>在 hda1 分区创建一个文件系统</td></tr><tr><td>mke2fs /dev/hda1</td><td>在 hda1 分区创建一个 linux ext2 的文件系统</td></tr><tr><td>mke2fs -j /dev/hda1</td><td>在 hda1 分区创建一个 linux ext3（日志型）的文件系统</td></tr><tr><td>mkfs -t vfat 32 -F /dev/hda1</td><td>创建一个 FAT32 文件系统</td></tr><tr><td>fdformat -n /dev/fd0</td><td>格式化一个软盘</td></tr><tr><td>mkswap /dev/hda3</td><td>创建一个 swap 文件系统</td></tr></tbody></table></div><h3 id="SWAP-文件系统"><a href="#SWAP-文件系统" class="headerlink" title="SWAP 文件系统"></a>SWAP 文件系统</h3><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>mkswap /dev/hda3</td><td>创建一个 swap 文件系统</td></tr><tr><td>swapon /dev/hda3</td><td>启用一个新的 swap 文件系统</td></tr><tr><td>swapon /dev/hda2 /dev/hdb3</td><td>启用两个 swap 分区</td></tr></tbody></table></div><h2 id="备份"><a href="#备份" class="headerlink" title="备份"></a>备份</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>dump -0aj -f /tmp/home0.bak /home</td><td>制作一个 &#39;/home&#39; 目录的完整备份</td></tr><tr><td>dump -1aj -f /tmp/home0.bak /home</td><td>制作一个 &#39;/home&#39; 目录的交互式备份</td></tr><tr><td>restore -if /tmp/home0.bak</td><td>还原一个交互式备份</td></tr><tr><td>rsync -rogpav --delete /home /tmp</td><td>同步两边的目录</td></tr><tr><td>rsync -rogpav -e ssh --delete /home ip_address:/tmp</td><td>通过 ssh 通道 rsync</td></tr><tr><td>rsync -az -e ssh --delete ip_addr:/home/public /home/local</td><td>通过 ssh 和压缩将一个远程目录同步到本地目录</td></tr><tr><td>rsync -az -e ssh --delete /home/local ip_addr:/home/public</td><td>通过 ssh 和压缩将本地目录同步到远程目录</td></tr><tr><td>dd bs=1M if=/dev/hda &vert; gzip &vert; ssh user@ip_addr &#39;dd of=hda.gz&#39;</td><td>通过 ssh 在远程主机上执行一次备份本地磁盘的操作</td></tr><tr><td>dd if=/dev/sda of=/tmp/file1</td><td>备份磁盘内容到一个文件</td></tr><tr><td>tar -Puf backup.tar /home/user</td><td>执行一次对 &#39;/home/user&#39; 目录的交互式备份操作</td></tr><tr><td>( cd /tmp/local/ &amp;&amp; tar c . ) &vert; ssh -C user@ip_addr &#39;cd /home/share/ &amp;&amp; tar x -p&#39;</td><td>通过 ssh 在远程目录中复制一个目录内容</td></tr><tr><td>( tar c /home ) &vert; ssh -C user@ip_addr &#39;cd /home/backup-home &amp;&amp; tar x -p&#39;</td><td>通过 ssh 在远程目录中复制一个本地目录</td></tr><tr><td>tar cf - . &vert; (cd /tmp/backup tar xf - )</td><td>本地将一个目录复制到另一个地方，保留原有权限及链接</td></tr><tr><td>find /home/user1 -name &#39;*.txt&#39; &vert; xargs cp -av --target-directory=/home/backup/ --parents</td><td>从一个目录查找并复制所有以 &#39;.txt&#39; 结尾的文件到另一个目录</td></tr><tr><td>find /var/log -name &#39;*.log&#39; &vert; tar cv --files-from=- &vert; bzip2 &gt; log.tar.bz2</td><td>查找所有以 &#39;.log&#39; 结尾的文件并做成一个 bzip 包</td></tr><tr><td>dd if=/dev/hda of=/dev/fd0 bs=512 count=1</td><td>做一个将 MBR（Master Boot Record）内容复制到软盘的动作</td></tr><tr><td>dd if=/dev/fd0 of=/dev/hda bs=512 count=1</td><td>从已经保存到软盘的备份中恢复 MBR 内容</td></tr></tbody></table></div><h2 id="光盘"><a href="#光盘" class="headerlink" title="光盘"></a>光盘</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>cdrecord -v gracetime=2 dev=/dev/cdrom -eject blank=fast -force</td><td>清空一个可复写的光盘内容</td></tr><tr><td>mkisofs /dev/cdrom &gt; cd.iso</td><td>在磁盘上创建一个光盘的 ISO 镜像文件</td></tr><tr><td>mkisofs /dev/cdrom &vert; gzip &gt; cd_iso.gz</td><td>在磁盘上创建一个压缩了的光盘 ISO 镜像文件</td></tr><tr><td>mkisofs -J -allow-leading-dots -R -V &quot;Label CD&quot; -iso-level 4 -o ./cd.iso data_cd</td><td>创建一个目录的 ISO 镜像文件</td></tr><tr><td>cdrecord -v dev=/dev/cdrom cd.iso</td><td>刻录一个 ISO 镜像文件</td></tr><tr><td>gzip -dc cd_iso.gz &vert; cdrecord dev=/dev/cdrom</td><td>刻录一个压缩了的 ISO 镜像文件</td></tr><tr><td>mount -o loop cd.iso /mnt/iso</td><td>挂载一个 ISO 镜像文件</td></tr><tr><td>cd-paranoia -B</td><td>从一个 CD 光盘转录音轨到 wav 文件中</td></tr><tr><td>cd-paranoia -- &quot;-3&quot;</td><td>从一个 CD 光盘转录音轨到 wav 文件中（参数-3）</td></tr><tr><td>cdrecord --scanbus</td><td>扫描总线以识别 scsi 通道</td></tr><tr><td>dd if=/dev/hdc &vert; md5sum</td><td>校验一个设备的 md5sum 编码，例如一张 CD</td></tr></tbody></table></div><h2 id="网络-（以太网和-WIFI-无线）"><a href="#网络-（以太网和-WIFI-无线）" class="headerlink" title="网络 - （以太网和 WIFI 无线）"></a>网络 - （以太网和 WIFI 无线）</h2><div class="table-container"><table><thead><tr><th>命令</th><th>描述</th></tr></thead><tbody><tr><td>ifconfig eth0</td><td>显示一个以太网卡的配置</td></tr><tr><td>ifup eth0</td><td>启用一个 &#39;eth0&#39; 网络设备</td></tr><tr><td>ifdown eth0</td><td>禁用一个 &#39;eth0&#39; 网络设备</td></tr><tr><td>ifconfig eth0 192.168.1.1 netmask 255.255.255.0</td><td>控制 IP 地址</td></tr><tr><td>ifconfig eth0 promisc</td><td>设置 &#39;eth0&#39; 成混杂模式以嗅探数据包（sniffing）</td></tr><tr><td>dhclient eth0</td><td>以 dhcp 模式启用 &#39;eth0&#39;</td></tr><tr><td>route -n show</td><td>显示路由</td></tr><tr><td>route add -net 0/0 gw 192.168.1.1</td><td>配置默认网关</td></tr><tr><td>route add -net 192.168.0.0 netmask 255.255.0.0 gw 192.168.1.1</td><td>配置静态路由为 &#39;192.168.0.0/16&#39;</td></tr><tr><td>route del 0/0 gw 192.168.1.1</td><td>删除静态路由</td></tr><tr><td>echo &quot;1&quot; &gt; /proc/sys/net/ipv4/ip_forward</td><td>激活 IP 路由</td></tr><tr><td>hostname</td><td>显示系统主机名</td></tr><tr><td>host www.example.com</td><td>查找主机名以将名称解析为 IP 地址，反之亦然</td></tr><tr><td>nslookup www.example.com</td><td>查找主机名以将名称解析为 IP 地址，反之亦然</td></tr><tr><td>ip link show</td><td>显示所有接口的链接状态</td></tr><tr><td>mii-tool eth0</td><td>显示 &#39;eth0&#39; 的链接状态</td></tr><tr><td>ethtool eth0</td><td>显示网卡 &#39;eth0&#39; 的统计信息</td></tr><tr><td>netstat -tup</td><td>显示所有活动的网络连接及其 PID</td></tr><tr><td>netstat -tupl</td><td>显示所有正在侦听系统的网络服务及其 PID</td></tr><tr><td>tcpdump tcp port 80</td><td>显示所有 HTTP 流量</td></tr><tr><td>iwlist scan</td><td>显示无线网络</td></tr><tr><td>iwconfig eth1</td><td>显示无线网卡的配置</td></tr><tr><td>whois www.example.com</td><td>在 Whois 数据库上查找</td></tr></tbody></table></div>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Linux/">Linux</category>
      
      
      <comments>https://blog.eurkon.com/post/817c7d82.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>MySQL 面试题解析</title>
      <link>https://blog.eurkon.com/post/54dbb40b.html</link>
      <guid>https://blog.eurkon.com/post/54dbb40b.html</guid>
      <pubDate>Wed, 14 Apr 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;CHAR、VARCHAR-的区别是什么？&quot;&gt;&lt;a href=&quot;#CHAR、VARCHAR-的区别是什么？&quot; class=&quot;headerlink&quot; title=&quot;CHAR、VARCHAR 的区别是什么？&quot;&gt;&lt;/a&gt;CHAR、VARCHAR 的区别是什么？&lt;/h2&gt;&lt;</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="CHAR、VARCHAR-的区别是什么？"><a href="#CHAR、VARCHAR-的区别是什么？" class="headerlink" title="CHAR、VARCHAR 的区别是什么？"></a>CHAR、VARCHAR 的区别是什么？</h2><ul><li>CHAR 的特点<ul><li>CHAR 表示定长字符串，长度是固定的；</li><li>如果插入数据的长度小于 CHAR 的固定长度时，则用空格填充；</li><li>因为长度固定，所以存取速度要比 VARCHAR 快很多，甚至能快 50%，但正因为其长度固定，所以会占据多余的空间，是空间换时间的做法；</li><li>对于 CHAR 来说，最多能存放的字符个数为 255，和编码无关。</li></ul></li><li>VARCHAR 的特点<ul><li>VARCHAR 表示可变长字符串，长度是可变的；</li><li>插入的数据是多长，就按照多长来存储；</li><li>VARCHAR 在存取方面与 CHAR 相反，它存取慢，因为长度不固定，但正因如此，不占据多余的空间，是时间换空间的做法；</li><li>对于 VARCHAR 来说，最多能存放的字符个数为 65532。</li></ul></li></ul><p>总之，结合性能角度（CHAR 更快）和节省磁盘空间角度（VARCHAR 更小），具体情况还需具体来设计数据库才是妥当的做法。</p><h2 id="DELETE、TRUNCATE-与-DROP-的区别是什么？"><a href="#DELETE、TRUNCATE-与-DROP-的区别是什么？" class="headerlink" title="DELETE、TRUNCATE 与 DROP 的区别是什么？"></a>DELETE、TRUNCATE 与 DROP 的区别是什么？</h2><ul><li>DELETE 命令从一个表中删除某一行，或多行；</li><li>TRUNCATE 命令永久地从表中删除每一行。</li><li>DROP 命令是删除表和表中所有数据；</li></ul><div class="table-container"><table><thead><tr><th>&nbsp;</th><th>DELETE</th><th>TRUNCATE</th><th>DROP</th></tr></thead><tbody><tr><td>类型</td><td>属于 DML</td><td>属于 DDL</td><td>属于 DDL</td></tr><tr><td>回滚</td><td>可回滚</td><td>不可回滚</td><td>不可回滚</td></tr><tr><td>删除内容</td><td>表结构还在，删除表的全部或者一部分数据</td><td>表结构还在，删除表中的所有数据</td><td>从数据库中删除表、所有的数据行、索引和权限也会被删除</td></tr><tr><td>删除速度</td><td>删除速度慢，需要逐行删除</td><td>删除速度快</td><td>删除速度最快</td></tr></tbody></table></div><h2 id="什么是触发器，MySQL-中都有哪些触发器？"><a href="#什么是触发器，MySQL-中都有哪些触发器？" class="headerlink" title="什么是触发器，MySQL 中都有哪些触发器？"></a>什么是触发器，MySQL 中都有哪些触发器？</h2><p>触发器是用户定义在关系表上的一类由事件驱动的特殊的存储过程。触发器是指一段代码，当触发某个事件时，自动执行这些代码。</p><p><strong>使用场景</strong></p><ul><li>可以通过数据库中的相关表实现级联更改。</li><li>实时监控某张表中的某个字段的更改而需要做出相应的处理。</li><li>例如可以生成某些业务的编号。</li><li>注意不要滥用，否则会造成数据库及应用程序的维护困难。</li></ul><p><strong>在 MySQL 数据库中有如下六种触发器：</strong></p><ul><li>Before Insert</li><li>After Insert</li><li>Before Update</li><li>After Update</li><li>Before Delete</li><li>After Delete</li></ul><h2 id="FLOAT-和-DOUBLE-的区别是什么？"><a href="#FLOAT-和-DOUBLE-的区别是什么？" class="headerlink" title="FLOAT 和 DOUBLE 的区别是什么？"></a>FLOAT 和 DOUBLE 的区别是什么？</h2><ul><li>FLOAT 类型数据可以存储至多 8 位十进制数，并在内存中占 4 字节。</li><li>DOUBLE 类型数据可以存储至多 18 位十进制数，并在内存中占 8 字节。</li></ul><h2 id="如何在-MySQL-中获取当前日期？"><a href="#如何在-MySQL-中获取当前日期？" class="headerlink" title="如何在 MySQL 中获取当前日期？"></a>如何在 MySQL 中获取当前日期？</h2><p><code>SELECT CURRENT_DATE();</code></p><h2 id="如何查询第-n-高的工资？"><a href="#如何查询第-n-高的工资？" class="headerlink" title="如何查询第 n 高的工资？"></a>如何查询第 n 高的工资？</h2><p><code>SELECT DISTINCT(salary) FROM employee ORDER BY salary DESC LIMIT n-1, 1</code></p><h2 id="请写出下面-MySQL-数据类型表达的意义（INT-0-、CHAR-16-、VARCHAR-16-、DATETIME、TEXT）"><a href="#请写出下面-MySQL-数据类型表达的意义（INT-0-、CHAR-16-、VARCHAR-16-、DATETIME、TEXT）" class="headerlink" title="请写出下面 MySQL 数据类型表达的意义（INT(0)、CHAR(16)、VARCHAR(16)、DATETIME、TEXT）"></a>请写出下面 MySQL 数据类型表达的意义（INT(0)、CHAR(16)、VARCHAR(16)、DATETIME、TEXT）</h2><p><strong>知识点分析</strong></p><p>此题考察的是 MySQL 数据类型。MySQL 数据类型属于 MySQL 数据库基础，由此延伸出的知识点还包括如下内容：</p><ul><li>MySQL 基础操作</li><li>MySQL 存储引擎</li><li>MySQL 锁机制</li><li>MySQL 事务处理、存储过程、触发器</li></ul><p><strong>数据类型考点：</strong></p><ol><li><p>整数类型，包括 TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT，分别表示 1 字节、2 字节、3 字节、4 字节、8 字节整数。任何整数类型都可以加上 <code>UNSIGNED</code> 属性，表示数据是无符号的，即非负整数。</p><p> 长度：整数类型可以被指定长度，例如：INT(11) 表示长度为 11 的 INT 类型。长度在大多数场景是没有意义的，它不会限制值的合法范围，只会影响显示字符的个数，而且需要和<code>UNSIGNED ZEROFILL</code> 属性配合使用才有意义。</p><p> 例子，假定类型设定为 INT(5)，属性为 <code>UNSIGNED ZEROFILL</code>，如果用户插入的数据为 12 的话，那么数据库实际存储数据为 00012。</p></li><li><p>实数类型，包括 FLOAT、DOUBLE、DECIMAL。</p><p> DECIMAL 可以用于存储比 BIGINT 还大的整型，能存储精确的小数。</p><p> 而 FLOAT 和 DOUBLE 是有取值范围的，并支持使用标准的浮点进行近似计算。</p><p> 计算时 FLOAT 和 DOUBLE 相比 DECIMAL 效率更高一些，DECIMAL 你可以理解成是用字符串进行处理。</p></li><li><p>字符串类型，包括 VARCHAR、CHAR、TEXT、BLOB。</p><p> VARCHAR 用于存储可变长字符串，它比定长类型更节省空间。</p><p> VARCHAR 使用额外 1 或 2 个字节存储字符串长度。列长度小于 255 字节时，使用 1 字节表示，否则使用 2 字节表示。</p><p> VARCHAR 存储的内容超出设置的长度时，内容会被截断。</p><p> CHAR 是定长的，根据定义的字符串长度分配足够的空间。</p><p> CHAR 会根据需要使用空格进行填充方便比较。</p><p> CHAR 适合存储很短的字符串，或者所有值都接近同一个长度。</p><p> CHAR 存储的内容超出设置的长度时，内容同样会被截断。</p><p> <strong>使用策略：</strong></p><p> 对于经常变更的数据来说，CHAR 比 VARCHAR 更好，因为 CHAR 不容易产生碎片。</p><p> 对于非常短的列，CHAR 比 VARCHAR 在存储空间上更有效率。</p><p> 使用时要注意只分配需要的空间，更长的列排序时会消耗更多内存。</p><p> 尽量避免使用 TEXT/BLOB 类型，查询时会使用临时表，导致严重的性能开销。</p></li><li><p>枚举类型（ENUM），把不重复的数据存储为一个预定义的集合。</p><p> 有时可以使用 ENUM 代替常用的字符串类型。</p><p> ENUM 存储非常紧凑，会把列表值压缩到一个或两个字节。</p><p> ENUM 在内部存储时，其实存的是整数。</p><p> 尽量避免使用数字作为 ENUM 枚举的常量，因为容易混乱。</p><p> 排序是按照内部存储的整数。</p></li><li><p>日期和时间类型，尽量使用 TIMESTAMP，空间效率高于 DATETIME，</p><p> 用整数保存时间戳通常不方便处理。</p><p> 如果需要存储微秒，可以使用 BIGINT 存储。</p></li></ol><p>看到这里，这道真题是不是就比较容易回答了。</p><p>答：INT(0) 表示数据是 INT 类型，长度是 0；CHAR(16) 表示固定长度字符串，长度为 16；VARCHAR(16) 表示可变长度字符串，长度为 16；DATETIME 表示时间类型；TEXT 表示字符串类型，能存储大字符串，最多存储 65535 字节数据。</p><h2 id="MySQL-基础操作"><a href="#MySQL-基础操作" class="headerlink" title="MySQL 基础操作"></a>MySQL 基础操作</h2><p>MySQL 的连接和关闭：<code>mysql -u -p -h -P</code></p><ul><li>-u：指定用户名</li><li>-p：指定密码</li><li>-h：主机</li><li>-P：端口</li></ul><p>进入 MySQL 命令行后：<code>G</code>、<code>c</code>、<code>q</code>、<code>s</code>、<code>h</code>、<code>d</code></p><ul><li>G：打印结果垂直显示</li><li>c：取消当前 MySQL 命令</li><li>q：退出 MySQL 连接</li><li>s：显示服务器状态</li><li>h：帮助信息</li><li>d：改变执行符</li></ul><h2 id="MySQL-存储引擎"><a href="#MySQL-存储引擎" class="headerlink" title="MySQL 存储引擎"></a>MySQL 存储引擎</h2><ul><li><p>InnoDB 存储引擎：</p><ul><li>默认事务型引擎，最重要最广泛的存储引擎，性能非常优秀。</li><li>数据存储在共享表空间，可以通过配置分开。也就是多个表和索引都存储在一个表空间中，可以通过配置文件改变此配置。</li><li>对主键查询的性能高于其他类型的存储引擎。</li><li>内部做了很多优化，从磁盘读取数据时会自动构建 hash 索引，插入数据时自动构建插入缓冲区。</li><li>通过一些机制和工具支持真正的热备份。</li><li>支持崩溃后的安全恢复。</li><li>支持行级锁。</li><li>支持外键。</li></ul></li><li><p>MyISAM 存储引擎：</p><ul><li>拥有全文索引、压缩、空间函数。</li><li>不支持事务和行级锁、不支持崩溃后的安全恢复。</li><li>表存储在两个文件，MYD 和 MYI。</li><li>设计简单，某些场景下性能很好，例如获取整个表有多少条数据，性能很高。</li><li>全文索引不是很常用，不如使用外部的 Elasticsearch 或 Lucene。</li></ul></li><li><p>其他表引擎：</p><ul><li>Archive、Blackhole、CSV、Memory</li></ul></li></ul><p><strong>使用策略：</strong>在大多数场景下建议使用 InnoDB 存储引擎。</p><h2 id="MySQL-锁机制"><a href="#MySQL-锁机制" class="headerlink" title="MySQL 锁机制"></a>MySQL 锁机制</h2><p>表锁是日常开发中的常见问题，因此也是面试当中最常见的考察点，当多个查询同一时刻进行数据修改时，就会产生并发控制的问题。共享锁和排他锁，就是读锁和写锁。</p><ul><li>共享锁，不堵塞，多个用户可以同时读一个资源，互不干扰。</li><li>排他锁，一个写锁会阻塞其他的读锁和写锁，这样可以只允许一个用户进行写入，防止其他用户读取正在写入的资源。</li></ul><p><strong>锁的粒度</strong></p><ul><li>表锁，系统开销最小，会锁定整张表，MyISAM 使用表锁。</li><li>行锁，最大程度的支持并发处理，但是也带来了最大的锁开销，InnoDB 使用行锁。</li></ul><h2 id="MySQL-事务处理"><a href="#MySQL-事务处理" class="headerlink" title="MySQL 事务处理"></a>MySQL 事务处理</h2><ul><li>MySQL 提供事务处理的表引擎，也就是 InnoDB。</li><li>服务器层不管理事务，由下层的引擎实现，所以同一个事务中，使用多种引擎是不靠谱的。</li><li>需要注意，在非事务表上执行事务操作，MySQL 不会发出提醒，也不会报错。</li></ul><h2 id="MySQL-存储过程"><a href="#MySQL-存储过程" class="headerlink" title="MySQL 存储过程"></a>MySQL 存储过程</h2><ul><li>为以后的使用保存的一条或多条 MySQL 语句的集合，因此也可以在存储过程中加入业务逻辑和流程。</li><li>可以在存储过程中创建表，更新数据，删除数据等等。</li></ul><p><strong>使用策略</strong></p><ul><li>可以通过把 SQL 语句封装在容易使用的单元中，简化复杂的操作；</li><li>可以保证数据的一致性；</li><li>可以简化对变动的管理。</li></ul><h2 id="MySQL-触发器"><a href="#MySQL-触发器" class="headerlink" title="MySQL 触发器"></a>MySQL 触发器</h2><p>提供给程序员和数据分析员来保证数据完整性的一种方法，它是与表事件相关的特殊的存储过程。</p><p><strong>使用场景</strong></p><ul><li>可以通过数据库中的相关表实现级联更改。</li><li>实时监控某张表中的某个字段的更改而需要做出相应的处理。</li><li>例如可以生成某些业务的编号。</li><li>注意不要滥用，否则会造成数据库及应用程序的维护困难。</li></ul><h2 id="请说明-InnoDB-和-MyISAM-的区别"><a href="#请说明-InnoDB-和-MyISAM-的区别" class="headerlink" title="请说明 InnoDB 和 MyISAM 的区别"></a>请说明 InnoDB 和 MyISAM 的区别</h2><ul><li>InnoDB 支持事务，MyISAM 不支持；</li><li>InnoDB 数据存储在共享表空间，MyISAM 数据存储在文件中；</li><li>InnoDB 支持行级锁，MyISAM 只支持表锁；</li><li>InnoDB 支持崩溃后的恢复，MyISAM 不支持；</li><li>InnoDB 支持外键，MyISAM 不支持；</li><li>InnoDB 不支持全文索引，MyISAM 支持全文索引。</li></ul><h2 id="InnoDB-引擎的特性"><a href="#InnoDB-引擎的特性" class="headerlink" title="InnoDB 引擎的特性"></a>InnoDB 引擎的特性</h2><ul><li>插入缓冲（insert buffer）</li><li>二次写（double write）</li><li>自适应哈希索引（ahi）</li><li>预读（read ahead）</li></ul><h2 id="请说明-VARCHAR-和-TEXT-的区别"><a href="#请说明-VARCHAR-和-TEXT-的区别" class="headerlink" title="请说明 VARCHAR 和 TEXT 的区别"></a>请说明 VARCHAR 和 TEXT 的区别</h2><ul><li>VARCHAR 可指定字符数，TEXT 不能指定，内部存储 VARCHAR 是存入的实际字符数 + 1 个字节（n&lt;=255）或 2 个字节(n&gt;255)，TEXT 是实际字符数 + 2 个字节。</li><li>TEXT 类型不能有默认值。</li><li>VARCHAR 可直接创建索引，TEXT 创建索引要指定前多少个字符。VARCHAR 查询速度快于 TEXT，在都创建索引的情况下，TEXT 的索引几乎不起作用。</li><li>查询 TEXT 需要创建临时表。</li></ul><h2 id="VARCHAR-50-中-50-的含义"><a href="#VARCHAR-50-中-50-的含义" class="headerlink" title="VARCHAR(50) 中 50 的含义"></a>VARCHAR(50) 中 50 的含义</h2><p>最多存放 50 个字符，VARCHAR(50) 和 VARCHAR(200) 存储 <code>hello</code> 所占空间一样，但后者在排序时会消耗更多内存，因为 <code>ORDER BY col</code> 采用 <code>fixed_length</code> 计算 <code>col</code> 长度（Memory 引擎也一样）。</p><h2 id="INT-20-中-20-的含义"><a href="#INT-20-中-20-的含义" class="headerlink" title="INT(20) 中 20 的含义"></a>INT(20) 中 20 的含义</h2><p>是指显示字符的长度，不影响内部存储，只是当定义了 <code>ZEROFILL</code> 时，前面补多少个 0。</p><h2 id="简单描述-MySQL-中，索引，主键，唯一索引，联合索引的区别，对数据库的性能有什么影响？"><a href="#简单描述-MySQL-中，索引，主键，唯一索引，联合索引的区别，对数据库的性能有什么影响？" class="headerlink" title="简单描述 MySQL 中，索引，主键，唯一索引，联合索引的区别，对数据库的性能有什么影响？"></a>简单描述 MySQL 中，索引，主键，唯一索引，联合索引的区别，对数据库的性能有什么影响？</h2><p><strong>知识点分析</strong></p><p>此题主要考察的是 MySQL 索引的基础和类型，由此延伸出的知识点还包括如下内容：</p><ul><li>MySQL 索引的创建原则</li><li>MySQL 索引的注意事项</li><li>MySQL 索引的原理</li></ul><p><strong>索引的基础</strong></p><ul><li>索引类似于书籍的目录，要想找到一本数的某个特定主题，需要先查找书的目录，定位对应的页码。</li><li>存储引擎使用类似的方式进行数据查询，先去索引当中找到对应的值，然后根据匹配的索引找到对应的数据行。</li></ul><p><strong>索引的使用场景</strong></p><ul><li>对于非常小的表，大部分情况下全表扫描效率更高。</li><li>中到大型表，索引非常有效。</li><li>特大型的表，建立和使用索引的代价会随之增大，可以使用分区技术来解决。</li></ul><p><strong>索引的类型</strong></p><p>索引很多种类型，是在 MySQL 的存储引擎实现的。</p><ul><li>普通索引：最基本的索引，没有任何约束限制。</li><li>唯一索引：和普通索引类似，但是具有唯一性约束。</li><li>主键索引：特殊的唯一索引，不允许有空值。</li></ul><p><strong>索引的区别</strong></p><ul><li>一个表只能有一个主键索引，但是可以有多个唯一索引。</li><li>主键索引一定是唯一索引，唯一索引不是主键索引。</li><li>主键可以与外键构成参照完整性约束，防止数据不一致。</li><li>联合索引：将多个列组合在一起创建索引，可以覆盖多个列。（也叫复合索引，组合索引）</li><li>外键索引：只有 InnoDB 类型的表才可以使用外键索引，保证数据的一致性、完整性、和实现级联操作（基本不用）。</li><li>全文索引：MySQL 自带的全文索引只能用于 MyISAM，并且只能对英文进行全文检索（基本不用）。</li></ul><p><strong>索引对性能的影响</strong></p><ul><li>大大减少服务器需要扫描的数据量。</li><li>帮助服务器避免排序和临时表。</li><li>将随机 I/O 变顺序 I/O。</li><li>大大提高查询速度。</li><li>降低写的速度（不良影响）。</li><li>磁盘占用（不良影响）。</li></ul><p><strong>创建索引的语法</strong></p><ol><li>首先创建一个表：<code>CREATE TABLE t1 (id INT PRIMARY KEY, username VARCHAR(20), password VARCHAR(20));</code></li><li>创建单个索引的语法：<code>CREATE INDEX 索引名 ON 表名(字段名)</code></li><li>索引名一般是：<code>表名_字段名</code></li><li>给 id 创建索引：<code>CREATE INDEX t1_id ON t1(id);</code></li><li>创建联合索引的语法：<code>CREATE INDEX 索引名 ON 表名(字段名 1，字段名 2)</code></li><li>给 username 和 password 创建联合索引：<code>CREATE INDEX t1_username_password ON t1(username, password)</code>，其中 INDEX 还可以替换成 UNIQUE，PRIMARY KEY，分别代表唯一索引和主键索引。</li><li>删除索引：<code>DROP INDEX t1_username_password ON t1</code></li></ol><p><strong>MySQL 索引的创建原则</strong></p><ul><li>最适合创建索引的列是出现在 <code>WHERE</code> 或 <code>ON</code> 子句中的列，或连接子句中的列而不是出现在 <code>SELECT</code> 关键字后的列。</li><li>索引列的基数越大，数据区分度越高，索引的效果越好。</li><li>对于字符串进行索引，应该制定一个前缀长度，可以节省大量的索引空间。</li><li>根据情况创建联合索引，联合索引可以提高查询效率。</li><li>避免创建过多的索引，索引会额外占用磁盘空间，降低写操作效率。</li><li>主键尽可能选择较短的数据类型，可以有效减少索引的磁盘占用提高查询效率。</li></ul><p><strong>MySQL 索引的注意事项</strong></p><ul><li><p>联合索引遵循前缀原则</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">KEY(a, b, c)</span><br><span class="line">WHERE a = 1 AND b = 2 AND c = 3</span><br><span class="line">WHERE a = 1 AND b = 2</span><br><span class="line">WHERE a = 1</span><br><span class="line"><span class="comment"># 以上 SQL 语句可以用到索引</span></span><br><span class="line">WHERE b = 2 AND c = 3</span><br><span class="line">WHERE a = 1 AND c = 3</span><br><span class="line"><span class="comment"># 以上 SQL 语句用不到索引</span></span><br></pre></td></tr></table></figure></p></li><li><p>LIKE 查询，<code>%</code> 不能在前</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">WHERE name LIKE &quot;%wang%&quot;</span><br><span class="line"><span class="comment"># 以上语句用不到索引，可以用外部的 Elasticsearch、Lucene 等全文搜索引擎替代。</span></span><br></pre></td></tr></table></figure></p></li><li><p>列值为空（NULL）时是可以使用索引的，但 MySQL 难以优化引用了可空列的查询，它会使索引、索引统计和值更加复杂。可空列需要更多的储存空间，还需要在 MySQL 内部进行特殊处理。</p></li><li><p>如果 MySQL 估计使用索引比全表扫描更慢，会放弃使用索引。</p><p>例如：表中只有 100 条数据左右。对于 SQL 语句 <code>WHERE id &gt; 1 AND id &lt; 100</code>，MySQL 会优先考虑全表扫描。</p></li><li><p>如果关键词 <code>OR</code> 前面的条件中的列有索引，后面的没有，所有列的索引都不会被用到。</p></li><li><p>列类型是字符串，查询时一定要给值加引号，否则索引失效。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 列 name VARCHAR(16)，存储了字符串 &quot;100&quot;。</span></span><br><span class="line">WHERE name = 100;</span><br><span class="line"><span class="comment"># 以上 SQL 语句能搜到，但无法用到索引。</span></span><br></pre></td></tr></table></figure></p></li></ul><p><strong>MySQL 索引的原理</strong></p><p>MySQL 索引是用一种叫做聚簇索引的数据结构实现的，下面我们就来看一下什么是聚簇索引。</p><p>聚簇索引是一种数据存储方式，它实际上是在同一个结构中保存了 B+ 树索引和数据行，InnoDB 表是按照聚簇索引组织的（类似于 Oracle 的索引组织表）。</p><p>InnoDB 通过主键聚簇数据，如果没有定义主键，会选择一个唯一的非空索引代替，如果没有这样的索引，会隐式定义个主键作为聚簇索引。</p><p><strong>注：</strong></p><ul><li>B+ 树是一种树数据结构，是一个 n 叉排序树，每个节点通常有多个孩子，一棵 B+ 树包含根节点、内部节点和叶子节点。根节点可能是一个叶子节点，也可能是一个包含两个或两个以上孩子节点的节点。</li><li>B+ 树通常用于数据库和操作系统的文件系统中。NTFS、ReiserFS、NSS、XFS、JFS、ReFS 和 BFS 等文件系统都在使用 B+ 树作为元数据索引。B+ 树的特点是能够保持数据稳定有序，其插入与修改拥有较稳定的对数时间复杂度。B+ 树元素自底向上插入。</li></ul><h2 id="列值为-NULL-时，查询是否会用到索引？"><a href="#列值为-NULL-时，查询是否会用到索引？" class="headerlink" title="列值为 NULL 时，查询是否会用到索引？"></a>列值为 NULL 时，查询是否会用到索引？</h2><p>在 MySQL 里 NULL 值的列也是走索引的。当然，如果计划对列进行索引，就要尽量避免把它设置为可空，MySQL 难以优化引用了可空列的查询，它会使索引、索引统计和值更加复杂。</p><h2 id="以下语句是否会应用索引：SELECT-FROM-users-WHERE-YEAR-adddate-lt-2007"><a href="#以下语句是否会应用索引：SELECT-FROM-users-WHERE-YEAR-adddate-lt-2007" class="headerlink" title="以下语句是否会应用索引：SELECT FROM users WHERE YEAR(adddate) &lt; 2007;"></a>以下语句是否会应用索引：SELECT FROM users WHERE YEAR(adddate) &lt; 2007;</h2><p>不会，因为只要列涉及到运算，MySQL 就不会使用索引。</p><h2 id="MyISAM-索引实现？"><a href="#MyISAM-索引实现？" class="headerlink" title="MyISAM 索引实现？"></a>MyISAM 索引实现？</h2><p>MyISAM 存储引擎使用 B+Tree 作为索引结构，叶节点的 data 域存放的是数据记录的地址。MyISAM 的索引方式也叫做非聚簇索引的，之所以这么称呼是为了与 InnoDB 的聚簇索引区分。</p><h2 id="MyISAM-索引与-InnoDB-索引的区别？"><a href="#MyISAM-索引与-InnoDB-索引的区别？" class="headerlink" title="MyISAM 索引与 InnoDB 索引的区别？"></a>MyISAM 索引与 InnoDB 索引的区别？</h2><ul><li>InnoDB 索引是聚簇索引，MyISAM 索引是非聚簇索引。</li><li>InnoDB 的主键索引的叶子节点存储着行数据，因此主键索引非常高效。</li><li>MyISAM 索引的叶子节点存储的是行数据地址，需要再寻址一次才能得到数据。</li><li>InnoDB 非主键索引的叶子节点存储的是主键和其他带索引的列数据，因此查询时做到覆盖索引会非常高效。</li></ul><h2 id="有-A-id-sex-par-c1-c2-、B-id-age-c1-c2-两张表，其中-A-id-与-B-id-关联，现在要求写出一条-SQL-语句，将-B-中-age-gt-50-的记录的-c1，c2-更新到-A-表中同一记录中的-c1，c2-字段中"><a href="#有-A-id-sex-par-c1-c2-、B-id-age-c1-c2-两张表，其中-A-id-与-B-id-关联，现在要求写出一条-SQL-语句，将-B-中-age-gt-50-的记录的-c1，c2-更新到-A-表中同一记录中的-c1，c2-字段中" class="headerlink" title="有 A(id, sex, par, c1, c2)、B(id, age, c1, c2) 两张表，其中 A.id 与 B.id 关联，现在要求写出一条 SQL 语句，将 B 中 age&gt;50 的记录的 c1，c2 更新到 A 表中同一记录中的 c1，c2 字段中"></a>有 A(id, sex, par, c1, c2)、B(id, age, c1, c2) 两张表，其中 A.id 与 B.id 关联，现在要求写出一条 SQL 语句，将 B 中 age&gt;50 的记录的 c1，c2 更新到 A 表中同一记录中的 c1，c2 字段中</h2><p><strong>考点分析</strong></p><p>这道题主要考察的是 MySQL 的关联 UPDATE 语句</p><p><strong>延伸考点</strong></p><ul><li>MySQL 的关联查询语句</li><li>MySQL 的关联 UPDATE 语句</li></ul><p>针对刚才这道题，答案可以是如下两种形式的写法：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">UPDATE</span> A, B <span class="keyword">SET</span> A.c1 = B.c1, A.c2 = B.c2 <span class="keyword">WHERE</span> A.id = B.id;</span><br><span class="line"><span class="keyword">UPDATE</span> A <span class="keyword">INNER</span> <span class="keyword">JOIN</span> B <span class="keyword">ON</span> A.id = B.id <span class="keyword">SET</span> A.c1 = B.c1, A.c2 = B.c2;</span><br><span class="line"><span class="comment"># 再加上 B 中 age &gt; 50 的条件：</span></span><br><span class="line"><span class="keyword">UPDATE</span> A, B <span class="keyword">set</span> A.c1 = B.c1, A.c2 = B.c2 <span class="keyword">WHERE</span> A.id = B.id <span class="keyword">and</span> B.age &gt; <span class="number">50</span>;</span><br><span class="line"><span class="keyword">UPDATE</span> A <span class="keyword">INNER</span> <span class="keyword">JOIN</span> B <span class="keyword">ON</span> A.id = B.id <span class="keyword">set</span> A.c1 = B.c1, A.c2 = B.c2 <span class="keyword">WHERE</span> B.age &gt; <span class="number">50</span>;</span><br></pre></td></tr></table></figure></p><h2 id="MySQL-的关联查询语句"><a href="#MySQL-的关联查询语句" class="headerlink" title="MySQL 的关联查询语句"></a>MySQL 的关联查询语句</h2><ul><li>交叉连接（CROSS JOIN）</li><li>内连接（INNER JOIN）</li><li>外连接（LEFT JOIN 或 RIGHT JOIN）</li><li>联合查询（UNION 与 UNION ALL）</li><li>全连接（FULL JOIN）</li><li>嵌套查询</li></ul><p><strong>交叉连接（CROSS JOIN）</strong><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> A, B(,C)</span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> A <span class="keyword">CROSS</span> <span class="keyword">JOIN</span> B (<span class="keyword">CROSS</span> <span class="keyword">JOIN</span> C)</span><br><span class="line"><span class="comment"># 没有任何关联条件，结果是笛卡尔积，结果集会很大，没有意义，很少使用</span></span><br></pre></td></tr></table></figure></p><p><strong>内连接（INNER JOIN）</strong><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> A, B <span class="keyword">WHERE</span> A.id = B.id</span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> A <span class="keyword">INNER</span> <span class="keyword">JOIN</span> B <span class="keyword">ON</span> A.id = B.id</span><br><span class="line"><span class="comment"># 多表中同时符合某种条件的数据记录的集合，INNER JOIN 可以缩写为 JOIN</span></span><br></pre></td></tr></table></figure></p><p>内连接分为三类</p><ul><li>等值连接：<code>ON A.id = B.id</code></li><li>不等值连接：<code>ON A.id &gt; B.id</code></li><li>自连接：<code>SELECT * FROM A T1 INNER JOIN A T2 ON T1.id=T2.pid</code></li></ul><p><strong>外连接（LEFT JOIN 或 RIGHT JOIN）</strong></p><ul><li>左外连接：<code>LEFT OUTER JOIN</code>，以左表为主，先查询出左表，按照 <code>ON</code> 后的关联条件匹配右表，没有匹配到的用 NULL 填充，可以简写成 <code>LEFT JOIN</code>。</li><li>右外连接：<code>RIGHT OUTER JOIN</code>，以右表为主，先查询出右表，按照 <code>ON</code> 后的关联条件匹配左表，没有匹配到的用 NULL 填充，可以简写成 <code>RIGHT JOIN</code>。</li></ul><p><strong>联合查询（UNION 与 UNION ALL）</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> A <span class="keyword">UNION</span> <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> B <span class="keyword">UNION</span> ...</span><br></pre></td></tr></table></figure></p><ul><li>就是把多个结果集集中在一起，<code>UNION</code> 前的结果为基准，需要注意的是联合查询的列数要相等，相同的记录行会合并</li><li>如果使用 <code>UNION ALL</code>，不会合并重复的记录行</li><li>效率 <code>UNION</code> 高于 <code>UNION ALL</code></li></ul><p><strong>全连接（FULL JOIN）</strong></p><p>MySQL 不支持全连接</p><p>可以使用 <code>LEFT JOIN</code> 和 <code>UNION</code> 和 <code>RIGHT JOIN</code> 联合使用</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> A <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> B <span class="keyword">ON</span> A.id=B.id <span class="keyword">UNION</span></span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> A <span class="keyword">RIGHT</span> <span class="keyword">JOIN</span> B <span class="keyword">ON</span> A.id=B.id</span><br></pre></td></tr></table></figure></p><p><strong>嵌套查询</strong></p><p>用一条 SQL 语句的结果作为另外一条 SQL 语句的条件，效率不好把握。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> A <span class="keyword">WHERE</span> <span class="keyword">id</span> <span class="keyword">IN</span> (<span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> B)</span><br></pre></td></tr></table></figure></p><p><strong>解题方法</strong></p><p>根据考题要搞清楚表的结果和多表之间的关系，根据想要的结果思考使用那种关联方式，通常把要查询的列先写出来，然后分析这些列都属于哪些表，才考虑使用关联查询</p><h2 id="MySQL-中-IN-和-EXISTS-区别"><a href="#MySQL-中-IN-和-EXISTS-区别" class="headerlink" title="MySQL 中 IN 和 EXISTS 区别"></a>MySQL 中 IN 和 EXISTS 区别</h2><p>MySQL 中的 IN 语句是把外表和内表作 hash 连接，而 EXISTS 语句是对外表作 loop 循环，每次 loop 循环再对内表进行查询。一直大家都认为 EXISTS 比 IN 语句的效率要高，这种说法其实是不准确的。这个是要区分环境的。</p><ul><li>如果查询的两个表大小相当，那么用 IN 和 EXISTS 差别不大。</li><li>如果两个表中一个较小，一个是大表，则子查询表大的用 EXISTS ，子查询表小的用 IN。</li><li>NOT IN 和 NOT EXISTS：如果查询语句使用了 NOT IN，那么内外表都进行全表扫描，没有用到索引；而 NOT EXISTS 的子查询依然能用到表上的索引。所以无论那个表大，用 NOT EXISTS 都比 NOT IN 要快。</li></ul><h2 id="一个-6-亿的表-A，一个-3-亿的表-B，通过外键-tid-关联，你如何最快的查询出满足条件的第-50000-到第-50200-中的这-200-条数据记录。"><a href="#一个-6-亿的表-A，一个-3-亿的表-B，通过外键-tid-关联，你如何最快的查询出满足条件的第-50000-到第-50200-中的这-200-条数据记录。" class="headerlink" title="一个 6 亿的表 A，一个 3 亿的表 B，通过外键 tid 关联，你如何最快的查询出满足条件的第 50000 到第 50200 中的这 200 条数据记录。"></a>一个 6 亿的表 A，一个 3 亿的表 B，通过外键 tid 关联，你如何最快的查询出满足条件的第 50000 到第 50200 中的这 200 条数据记录。</h2><ul><li><p>如果 A 表 TID 是自增长，并且是连续的，B 表的 ID 为索引</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> a, b <span class="keyword">WHERE</span> a.tid = b.id <span class="keyword">AND</span> a.tid &gt; <span class="number">50000</span> <span class="keyword">LIMIT</span> <span class="number">200</span>;</span><br></pre></td></tr></table></figure></p></li><li><p>如果 A 表的 TID 不是连续的，那么就需要使用覆盖索引，tid 要么是主键，要么是辅助索引，B 表 id 也需要有索引。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> b, (<span class="keyword">SELECT</span> tid <span class="keyword">FROM</span> a <span class="keyword">LIMIT</span> <span class="number">50000</span>, <span class="number">200</span>) a <span class="keyword">WHERE</span> b.id = a .tid;</span><br></pre></td></tr></table></figure></p></li></ul><h2 id="拷贝表（拷贝数据，源表名：a-，目标表名：b）"><a href="#拷贝表（拷贝数据，源表名：a-，目标表名：b）" class="headerlink" title="拷贝表（拷贝数据，源表名：a ，目标表名：b）"></a>拷贝表（拷贝数据，源表名：a ，目标表名：b）</h2><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> b(a, b, c) <span class="keyword">SELECT</span> d, e, f <span class="keyword">FROM</span> a;</span><br></pre></td></tr></table></figure></p><h2 id="Student-S-Sname-Sage-Ssex-学生表-Course-C-Cname-T-课程表-SC-S-C-score-成绩表-Teacher-T-Tname-教师表，查询没学过“叶平”老师课的同学的学号、姓名"><a href="#Student-S-Sname-Sage-Ssex-学生表-Course-C-Cname-T-课程表-SC-S-C-score-成绩表-Teacher-T-Tname-教师表，查询没学过“叶平”老师课的同学的学号、姓名" class="headerlink" title="Student(S#, Sname, Sage, Ssex) 学生表 Course(C#, Cname, T#) 课程表 SC(S#, C#, score) 成绩表 Teacher(T#, Tname) 教师表，查询没学过“叶平”老师课的同学的学号、姓名"></a>Student(S#, Sname, Sage, Ssex) 学生表 Course(C#, Cname, T#) 课程表 SC(S#, C#, score) 成绩表 Teacher(T#, Tname) 教师表，查询没学过“叶平”老师课的同学的学号、姓名</h2><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> Student.S<span class="comment">#, Student.Sname</span></span><br><span class="line">  <span class="keyword">FROM</span> Student</span><br><span class="line"> <span class="keyword">WHERE</span> S<span class="comment"># NOT IN (SELECT DISTINCT (SC.S#)</span></span><br><span class="line">                    <span class="keyword">FROM</span> SC, Course, Teacher</span><br><span class="line">                   <span class="keyword">WHERE</span> SC.C<span class="comment"># = Course.C#</span></span><br><span class="line">                     <span class="keyword">AND</span> Teacher.T<span class="comment"># = Course.T#</span></span><br><span class="line">                     <span class="keyword">AND</span> Teacher.Tname = <span class="string">&#x27;叶平&#x27;</span>);</span><br></pre></td></tr></table></figure></p><h2 id="随机取出-10-条数据"><a href="#随机取出-10-条数据" class="headerlink" title="随机取出 10 条数据"></a>随机取出 10 条数据</h2><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> *</span><br><span class="line">  <span class="keyword">FROM</span> <span class="keyword">users</span></span><br><span class="line"> <span class="keyword">WHERE</span> <span class="keyword">id</span> &gt;= ((<span class="keyword">SELECT</span> <span class="keyword">MAX</span>(<span class="keyword">id</span>) <span class="keyword">FROM</span> <span class="keyword">users</span>) - (<span class="keyword">SELECT</span> <span class="keyword">MIN</span>(<span class="keyword">id</span>) <span class="keyword">FROM</span> <span class="keyword">users</span>)) *</span><br><span class="line">       <span class="keyword">RAND</span>() + (<span class="keyword">SELECT</span> <span class="keyword">MIN</span>(<span class="keyword">id</span>) <span class="keyword">FROM</span> <span class="keyword">users</span>) <span class="keyword">LIMIT</span> <span class="number">10</span></span><br><span class="line"><span class="comment"># 此方法效率比直接用 SELECT * FROM users ORDER BY RAND() LIMIT 10 高很多</span></span><br></pre></td></tr></table></figure></p><h2 id="请简述项目中优化-SQL-语句执行效率的方法，从哪些方面，SQL-语句性能如何分析？"><a href="#请简述项目中优化-SQL-语句执行效率的方法，从哪些方面，SQL-语句性能如何分析？" class="headerlink" title="请简述项目中优化 SQL 语句执行效率的方法，从哪些方面，SQL 语句性能如何分析？"></a>请简述项目中优化 SQL 语句执行效率的方法，从哪些方面，SQL 语句性能如何分析？</h2><p><strong>考点分析</strong></p><p>这道题主要考察的是查找分析 SQL 语句查询速度慢的方法</p><p><strong>延伸考点</strong></p><ul><li>优化查询过程中的数据访问</li><li>优化长难的查询语句</li><li>优化特定类型的查询语句</li></ul><p><strong>如何查找查询速度慢的原因</strong></p><p>记录慢查询日志，分析查询日志，不要直接打开慢查询日志进行分析，这样比较浪费时间和精力，可以使用 pt-query-digest 工具进行分析。</p><ul><li>使用 <code>show profile</code><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> profiling=<span class="number">1</span>;</span><br><span class="line"><span class="comment"># 开启，服务器上所有执行语句会记录执行时间，存到临时表中</span></span><br><span class="line"><span class="keyword">show</span> <span class="keyword">profiles</span></span><br><span class="line"><span class="keyword">show</span> profile <span class="keyword">for</span> <span class="keyword">query</span> 临时表 <span class="keyword">ID</span></span><br></pre></td></tr></table></figure></p></li><li>使用 <code>show status</code><code>show status</code> 会返回一些计数器，<code>show global status</code> 会查看所有服务器级别的所有计数，有时根据这些计数，可以推测出哪些操作代价较高或者消耗时间多。</li><li><code>show processlist</code>观察是否有大量线程处于不正常的状态或特征</li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/mysql_precesslist.png" alt="processlist"></p><ul><li>使用 <code>explain</code> 分析单条 SQL 语句</li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/mysql_explain.png" alt="explain"></p><p><strong>id</strong> 由一组数字组成。表示一个查询中各个子查询的执行顺序。</p><ul><li>id 相同执行顺序由上至下。</li><li>id 不同，id 值越大优先级越高，越先被执行。</li><li>id 为 NULL 时表示一个结果集，不需要使用它查询，常出现在包含 UNION 等查询语句中。</li></ul><p><strong>select_type</strong> 每个子查询的查询类型，一些常见的查询类型。</p><div class="table-container"><table><thead><tr><th>id</th><th>select_type</th><th>description</th></tr></thead><tbody><tr><td>1</td><td>SIMPLE</td><td>不包含任何子查询或 UNION 等查询</td></tr><tr><td>2</td><td>PRIMARY</td><td>包含子查询最外层查询就显示为 PRIMARY</td></tr><tr><td>3</td><td>SUBQUERY</td><td>在 SELECT 或 WHERE 子句中包含的查询</td></tr><tr><td>4</td><td>DERIVED</td><td>FROM 子句中包含的查询</td></tr><tr><td>5</td><td>UNION</td><td>出现在 UNION 后的查询语句中</td></tr><tr><td>6</td><td>UNION RESULT</td><td>从 UNION 中获取结果集</td></tr></tbody></table></div><p><strong>type</strong> 访问类型（非常重要，可以看到有没有走索引）</p><ul><li>ALL：扫描全表数据</li><li>index：遍历索引</li><li>range：索引范围查找</li><li>index_subquery：在子查询中使用 ref</li><li>unique_subquery：在子查询中使用 eq_ref</li><li>ref_or_null：对 NULL 进行索引的优化的 ref</li><li>fulltext：使用全文索引</li><li>ref：使用非唯一索引查找数据</li><li>eq_ref：在 JOIN 查询中使用 PRIMARY KEY 或 UNIQUE NOT NULL 索引关联。</li></ul><p><strong>possible_keys</strong> 可能使用的索引，注意不一定会使用。查询涉及到的字段上若存在索引，则该索引将被列出来。当该列为 NULL 时就要考虑当前的 SQL 是否需要优化了。</p><p><strong>key</strong> 显示 MySQL 在查询中实际使用的索引，若没有使用索引，显示为 NULL。查询中若使用了覆盖索引（索引的数据覆盖了需要查询的所有数据），则该索引仅出现在 key 列表中。</p><p><strong>key_length</strong> 索引长度</p><ul><li>ref 表示上述表的连接匹配条件，即哪些列或常量被用于查找索引列上的值</li><li>rows 返回估算的结果集数目，并不是一个准确的值。</li><li>extra 的信息非常丰富，常见的有：<ul><li>Using index 使用覆盖索引</li><li>Using where 使用了用 where 子句来过滤结果集</li><li>Using filesort 使用文件排序，使用非索引列进行排序时出现，非常消耗性能，尽量优化。</li><li>Using temporary 使用了临时表 sql 优化的目标可以参考阿里开发手册</li></ul></li></ul><p><strong>推荐：</strong>SQL 性能优化的目标至少要达到 range 级别，要求是 ref 级别，如果可以是 consts 最好。</p><ul><li>consts：单表中最多只有一个匹配行（主键或者唯一索引），在优化阶段即可读取到数据。</li><li>ref：指的是使用普通的索引（normal index）。</li><li>range：对索引进行范围检索。</li></ul><p>反例：explain 表的结果，<code>type=index</code>，索引物理文件全扫描，速度非常慢，这个 index 级别比较 range 还低，与全表扫描是小巫见大巫。</p><p><strong>优化查询过程中的数据访问</strong></p><ul><li>访问数据太多导致查询性能下降</li><li>确定应用程序是否在检索大量超过需要的数据，可能是太多行或列</li><li>确认 MySQL 服务器是否在分析大量不必要的数据行</li><li>避免犯如下 SQL 语句错误<ul><li>查询不需要的数据。解决办法：使用 LIMIT 解决。</li><li>多表关联返回全部列。解决办法：指定列名。</li><li>总是返回全部列。解决办法：避免使用 SELECT *。</li></ul></li><li>重复查询相同的数据。解决办法：可以缓存数据，下次直接读取缓存。</li><li>是否在扫描额外的记录。解决办法：使用 explain 进行分析，如果发现查询需要扫描大量的数据，但只返回少数的行。</li><li>使用索引覆盖扫描，把所有的列都放到索引中，这样存储引擎不需要回表获取对应行就可以返回结果。</li><li>改变数据库和表的结构，修改数据表范式。</li><li>重写 SQL 语句，让优化器可以以更优的方式执行查询。</li></ul><p><strong>优化长难的查询语句</strong></p><ul><li>一个复杂查询还是多个简单查询。</li><li>MySQL 内部每秒能扫描内存中上百万行数据，相比之下，响应数据给客户端就要慢得多。</li><li>使用尽可能小的查询是好的，但是有时将一个大的查询分解为多个小的查询是很有必要的。</li><li>切分查询，将一个大的查询分为多个小的相同的查询。</li><li>一次性删除 1000 万的数据要比一次删除 1 万快，暂停一会的方案更加损耗服务器开销。</li><li>分解关联查询，让缓存的效率更高。</li><li>执行单个查询可以减少锁的竞争。</li><li>在应用层做关联更容易对数据库进行拆分。</li><li>查询效率会有大幅提升。</li><li>较少冗余记录的查询。</li></ul><p><strong>优化特定类型的查询语句</strong></p><ul><li>COUNT(*) 会忽略所有的列，直接统计所有列数，不要使用 COUNT(列名)。</li><li>MyISAM 中，没有任何 WHERE 条件的 COUNT(*) 非常快。</li><li>当有 WHERE 条件时，MyISAM 的 COUNT 统计不一定比其它引擎快。</li><li>可以使用 <code>explain</code> 查询近似值，用近似值替代 COUNT(*)。</li><li>增加汇总表。</li><li>使用缓存。</li></ul><p><strong>优化关联查询</strong></p><ul><li>确定 ON 或者 USING 子句中是否有索引。</li><li>确保 GROUP BY 和 ORDER BY 只有一个表中的列，这样 MySQL 才有可能使用索引。</li></ul><p><strong>优化子查询</strong></p><ul><li>用关联查询替代。</li><li>优化 GROUP BY 和 DISTINCT。</li><li>这两种查询可以使用索引来优化，是最有效的优化方法。</li><li>关联查询中，使用标识列分组的效率更高。</li><li>如果不需要 ORDER BY，进行 GROUP BY 时加 ORDER BY NULL，MySQL 不会再进行文件排序。</li><li>WITH ROLLUP 超级聚合，可以挪到应用程序处理。</li></ul><p><strong>优化 LIMIT 分页</strong></p><ul><li>LIMIT 偏移量大的时候，查询效率较低</li><li>可以记录上次查询的最大 ID，下次查询时直接根据该 ID 来查询</li></ul><p><strong>优化 UNION 查询</strong></p><ul><li>UNION ALL 的效率高于 UNION</li></ul><p><strong>解题方法</strong></p><p>对于此类考题，先说明如何定位低效 SQL 语句，然后根据 SQL 语句可能低效的原因做排查，先从索引着手，如果索引没有问题，考虑以上几个方面，数据访问的问题，长难查询句的问题还是一些特定类型优化的问题，逐一回答。</p><h2 id="SQL-语句优化的一些方法？"><a href="#SQL-语句优化的一些方法？" class="headerlink" title="SQL 语句优化的一些方法？"></a>SQL 语句优化的一些方法？</h2><ul><li>对查询进行优化，应尽量避免全表扫描，首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引。</li><li><p>应尽量避免在 WHERE 子句中对字段进行 NULL 值判断，否则将导致引擎放弃使用索引而进行全表扫描，如：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">num</span> <span class="keyword">is</span> <span class="literal">NULL</span></span><br><span class="line"><span class="comment"># 可以在 num 上设置默认值 0，确保表中 num 列没有 NULL 值，然后这样查询：</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">num</span> = <span class="number">0</span></span><br></pre></td></tr></table></figure></p></li><li><p>应尽量避免在 WHERE 子句中使用 <code>!=</code> 或 <code>&lt;&gt;</code> 操作符，否则引擎将放弃使用索引而进行全表扫描。</p></li><li><p>应尽量避免在 WHERE 子句中使用 OR 来连接条件，否则将导致引擎放弃使用索引而进行全表扫描，如：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">num</span> = <span class="number">10</span> <span class="keyword">OR</span> <span class="keyword">num</span> = <span class="number">20</span></span><br><span class="line"><span class="comment"># 可以这样查询：</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">num</span> = <span class="number">10</span> <span class="keyword">UNION</span> <span class="keyword">ALL</span> <span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">num</span> = <span class="number">20</span></span><br></pre></td></tr></table></figure></p></li><li><p>IN 和 NOT IN 也要慎用，否则会导致全表扫描，如：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">num</span> <span class="keyword">IN</span> (<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>) </span><br><span class="line">对于连续的数值，能用 <span class="keyword">BETWEEN</span> 就不要用 <span class="keyword">IN</span> 了：</span><br><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">num</span> <span class="keyword">BETWEEN</span> <span class="number">1</span> <span class="keyword">AND</span> <span class="number">3</span></span><br></pre></td></tr></table></figure></p></li><li><p>下面的查询也将导致全表扫描：<code>SELECT id FROM t WHERE name LIKE &#39;%李%&#39;</code> 若要提高效率，可以考虑全文检索。</p></li><li><p>如果在 WHERE 子句中使用参数，也会导致全表扫描。因为 SQL 只有在运行时才会解析局部变量，但优化程序不能将访问计划的选择推迟到运行时；它必须在编译时进行选择。然而，如果在编译时建立访问计划，变量的值还是未知的，因而无法作为索引选择的输入项。如下面语句将进行全表扫描：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">num</span>=@<span class="keyword">num</span></span><br><span class="line">可以改为强制查询使用索引：</span><br><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WITH</span>(<span class="keyword">INDEX</span>(索引名)) <span class="keyword">WHERE</span> <span class="keyword">num</span>=@<span class="keyword">num</span></span><br></pre></td></tr></table></figure></p></li><li><p>应尽量避免在 WHERE 子句中对字段进行表达式操作，这将导致引擎放弃使用索引而进行全表扫描。如：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">num</span>/<span class="number">2</span>=<span class="number">100</span></span><br><span class="line"><span class="comment"># 应改为:</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">num</span>=<span class="number">100</span>*<span class="number">2</span></span><br></pre></td></tr></table></figure></p></li><li><p>应尽量避免在 WHERE 子句中对字段进行函数操作，这将导致引擎放弃使用索引而进行全表扫描。如：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">substring</span>(<span class="keyword">name</span>, <span class="number">1</span>, <span class="number">3</span>) = <span class="string">&#x27;abc&#x27;</span></span><br><span class="line">以 abc 开头的 <span class="keyword">id</span> 应改为:</span><br><span class="line"><span class="keyword">SELECT</span> <span class="keyword">id</span> <span class="keyword">FROM</span> t <span class="keyword">WHERE</span> <span class="keyword">name</span> <span class="keyword">LIKE</span> <span class="string">&#x27;abc%&#x27;</span></span><br></pre></td></tr></table></figure></p></li><li>不要在 WHERE 子句中的 <code>=</code> 左边进行函数、算术运算或其他表达式运算，否则系统将可能无法正确使用索引。</li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/MySQL/">MySQL</category>
      
      
      <comments>https://blog.eurkon.com/post/54dbb40b.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Sqoop 面试题解析</title>
      <link>https://blog.eurkon.com/post/420614eb.html</link>
      <guid>https://blog.eurkon.com/post/420614eb.html</guid>
      <pubDate>Tue, 13 Apr 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Sqoop-参数&quot;&gt;&lt;a href=&quot;#Sqoop-参数&quot; class=&quot;headerlink&quot; title=&quot;Sqoop 参数&quot;&gt;&lt;/a&gt;Sqoop 参数&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Sqoop 导入数据到 HDFS 中的参数&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Sqoop-参数"><a href="#Sqoop-参数" class="headerlink" title="Sqoop 参数"></a>Sqoop 参数</h2><p><strong>Sqoop 导入数据到 HDFS 中的参数</strong></p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">/opt/module/sqoop/bin/sqoop import \</span><br><span class="line">--connect jdbc 的 url 字符串\</span><br><span class="line">--username 账号\</span><br><span class="line">--password 密码\</span><br><span class="line"><span class="meta">#</span><span class="bash"> HDFS 目标的目录</span></span><br><span class="line">--target-dir \</span><br><span class="line"><span class="meta">#</span><span class="bash"> 导入的目标目录如果存在则删除那个目录</span></span><br><span class="line">--delete-target-dir \</span><br><span class="line"><span class="meta">#</span><span class="bash"> 相当于 -m，并行导入时 MapTask 的个数</span></span><br><span class="line">--num-mappers \</span><br><span class="line">--fields-terminated-by \</span><br><span class="line"><span class="meta">#</span><span class="bash"> 指定满足 sql 和条件的数据导入</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> --query：增加检索条件部分数据抽取</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> <span class="variable">$CONDITIONS</span>：数据分割条件的占位符</span></span><br><span class="line">--query &quot;$2&quot; &#x27;and $CONDITIONS;&#x27;</span><br></pre></td></tr></table></figure></p><p><strong>Sqoop 导入数据到 Hive 中的参数</strong></p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 一步将表结构和数据都导入到 hive 中</span></span><br><span class="line">bin/sqoop import \</span><br><span class="line">--connect jdbc 的 url 字符串\</span><br><span class="line">--table mysql 中的表名\</span><br><span class="line">--username 账号\</span><br><span class="line">--password 密码\</span><br><span class="line">--hive-import \</span><br><span class="line">--m MapTask 的个数 \</span><br><span class="line">--hive-database hive 中的数据库名 \</span><br></pre></td></tr></table></figure></p><p><strong>RDBMS 中的增量数据如何导入</strong></p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 指定判断检查的依据字段</span></span><br><span class="line">--check-column 字段名 \</span><br><span class="line"><span class="meta">#</span><span class="bash"> 用来指定增量当如的模式（Mode），append 和 lastmodified</span></span><br><span class="line">--incremental 导入模式 \</span><br><span class="line">--last-value 上一次导入结束的时间 \</span><br><span class="line">--m MapTask 的个数 \</span><br><span class="line">--merge-key 主键</span><br></pre></td></tr></table></figure></p><p><strong>补充：</strong>使用 <code>--merge-key</code> 合并模式，如果是新增的数据则增加，因为 <code>incremental</code> 是 <code>lastmodified</code> 模式，那么当有数据更新了，而主键没有变，则会进行合并。</p><p><code>--check-column</code> 字段名：当数据更新和修改，这个字段的时间也要随之变化，MySQL 中建表时，该字段修饰符、字段名 <code>timeatamp default current_timestamp on update current_timestamp</code></p><h2 id="Sqoop-导入导出-NULL-存储一致性问题"><a href="#Sqoop-导入导出-NULL-存储一致性问题" class="headerlink" title="Sqoop 导入导出 NULL 存储一致性问题"></a>Sqoop 导入导出 NULL 存储一致性问题</h2><p>Hive 中的 NULL 在底层是以 <code>\N</code> 来存储的，而 MySQL 中的 NULL 在底层就是 NULL</p><p>为了保证数据两端的一致性</p><ul><li>在导出数据时采用 <code>--input-null-string</code> 和 <code>--input-null-non-string</code> 两个参数</li><li>在导入数据时采用 <code>--null-string</code> 和 <code>--null-non-string</code></li></ul><h2 id="Sqoop-数据导出一致性问题"><a href="#Sqoop-数据导出一致性问题" class="headerlink" title="Sqoop 数据导出一致性问题"></a>Sqoop 数据导出一致性问题</h2><p><strong>场景：</strong></p><p>如果 Sqoop 在导出数据到 MySQL 时，使用 4 个 Map 任务，过程中有 2 个任务失败，此时 MySQL 中存储了另外两个 Map 任务导入的数据，但是此时老板正好看到了这个报表数据。而开发工程师发现任务失败后，会调试问题并最终将全部数据正确的导入到 MySQL，那在后面老板再次看报表数据时，发现本次看到的数据与之前的不一致，这在生产环境中式不允许的。</p><p><strong>解决方案一：</strong></p><p>因为 Sqoop 将导出过程分解为多个事务，所以一个失败的导出作业可能会导致部分数据被提交到数据库。在某些情况下，这可能进一步导致后续作业由于插入冲突而失败，或者在其他情况下导致重复数据。您可以通过 <code>--stage-table</code> 选项指定一个暂存表（staging）来解决这个问题，这个表充当一个辅助表，用于暂存导出的数据。阶段数据最终在一个事务中移动到目标表。</p><blockquote><p>官网：<a href="http://sqoop.apache.org/docs/1.4.6/SqoopUserGuide.html">http://sqoop.apache.org/docs/1.4.6/SqoopUserGuide.html</a><br>Since Sqoop breaks down export process into multiple transactions, it is possible that a failed export job may result in partial data being committed to the database. This can further lead to subsequent jobs failing due to insert collisions in some cases, or lead to duplicated data in others. You can overcome this problem by specifying a staging table via the --staging-table option which acts as an auxiliary table that is used to stage exported data. The staged data is finally moved to the destination table in a single transaction.</p></blockquote><p>使用 <code>--stage-table</code> 选项，将 HDFS 中的数据先导入到辅助表中，当 HDFS 中的数据导出成功后，辅助表中的数据在一个事务中导出到目标表中（也就是说，这个过程要么完全成功，要么完全失败）</p><p>为了能够使用 staging 这个选项，staging 表在运行任务前可能是空的，如果不为空就使用 <code>--clear-staging-table</code> 配置</p><p>如果 staging 表中有数据，并且使用了 <code>--clear-staging-table</code> 选项，Sqoop 执行导出任务前会删除 staging 表中所有的数据</p><p><strong>注意：</strong><code>--direct</code> 导入时 staging 方式是不可用的，使用了 <code>--update-key</code> 选项时 staging 方式也不可用</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">sqoop export \</span><br><span class="line">--connect url \</span><br><span class="line">--username root \</span><br><span class="line">--password 123456 \</span><br><span class="line">--table app_cource_study_report \</span><br><span class="line">--columns watch_video_cnt,complete_video_cnt,dt \</span><br><span class="line">--fields-terminated-by &quot;\t&quot; \</span><br><span class="line">--export-dir &quot;/user/hive/warehouse/tmp.db/app_cource_study_analysis_$&#123;day&#125;&quot; \</span><br><span class="line">--staging-table app_cource_study_report_tmp \</span><br><span class="line">--clear-staging-table \</span><br><span class="line">--input-null-string &#x27;\\N&#x27; \</span><br><span class="line">--null-non-string &#x27;\\N&#x27;</span><br></pre></td></tr></table></figure></p><p><strong>解决方案二：</strong></p><p>设置 map 数量为 1 个（不推荐，面试官想要的答案不是这个）。</p><p>多个 Map 任务时，采用 <code>--staging-table</code> 方式，仍然可以解决数据一致性问题。</p><h2 id="Sqoop-底层运行的任务是什么？"><a href="#Sqoop-底层运行的任务是什么？" class="headerlink" title="Sqoop 底层运行的任务是什么？"></a>Sqoop 底层运行的任务是什么？</h2><p>只有 Map 阶段，没有 Reduce 阶段的任务。默认是 4 个 MapTask。</p><h2 id="MapTask-并行度设置大于-1-的问题？"><a href="#MapTask-并行度设置大于-1-的问题？" class="headerlink" title="MapTask 并行度设置大于 1 的问题？"></a>MapTask 并行度设置大于 1 的问题？</h2><p>并行度导入数据的时候需要指定根据哪个字段进行切分，该字段通常是主键或者是自增长不重复的数值类型字段，否则会报下面的错误：</p><p><code>Import failed: No primary key could be found for table. Please specify one with --split-by or perform a sequential import with &#39;-m 1&#39;.</code></p><p>那么就是说当 MapTask 并行度大于 1 时，下面两个参数要同时使用：</p><ul><li><code>--split-by id</code>：指定根据 id 字段进行切分；</li><li><code>--m n</code>：指定 map 并行度 n 个。</li></ul><h2 id="Sqoop-一天导入多少数据？"><a href="#Sqoop-一天导入多少数据？" class="headerlink" title="Sqoop 一天导入多少数据？"></a>Sqoop 一天导入多少数据？</h2><p>每天 100 万活跃 --&gt; 10 万订单，1 人 10 条，每天 1g 左右业务数据</p><p>Sqoop 每天将 1G 的数据量导入到数仓</p><h2 id="Sqoop-数据导出的时候一次执行多长时间？"><a href="#Sqoop-数据导出的时候一次执行多长时间？" class="headerlink" title="Sqoop 数据导出的时候一次执行多长时间？"></a>Sqoop 数据导出的时候一次执行多长时间？</h2><p>每天晚上 00：30 开始执行，Sqoop 任务一般情况 40-50 分钟的都有，取决于数据量（11.11，6.18 等活动在 1 个小时左右）。</p><p>Sqoop 任务 5 分钟 ~ 2 小时的都有，取决于数据量。</p><h2 id="Sqoop-在导入数据的时候数据倾斜？"><a href="#Sqoop-在导入数据的时候数据倾斜？" class="headerlink" title="Sqoop 在导入数据的时候数据倾斜？"></a>Sqoop 在导入数据的时候数据倾斜？</h2><p>在说数据倾斜之前，我们设想数据并行抽数最理想的情况就是所有的并行实例同时完成，木桶原理，数据抽数时间的长短取决于耗时最长的那个实例。</p><p>由于各个实例被分配的数据量不均而导致分配到数据量较少的实例早早完成，而被分配大量数据的实例迟迟无法完成，导致整个作业运行时间很长的现象就是数据倾斜造成的。</p><p>数据倾斜概括为一句话：<strong>数据分配不均</strong>。</p><p>在数据表没有符合要求的字段的情况下，我们需要自己临时简单创建理想的字段：</p><p>Sqoop 抽数的并行化主要设计到两个参数：</p><ul><li><code>--num-mappers</code>：启动 n 个 map 来并行导入数据，默认 4 个；</li><li><code>--split-by</code>：按照某一列来切分表的工作单元。</li></ul><p>具体原理是通过 <code>ROWNUM()</code> 生成一个严格均匀分布的字段，然后指定为分割字段。</p><h2 id="Sqoop-数据导出-Parquet（项目中遇到的问题）"><a href="#Sqoop-数据导出-Parquet（项目中遇到的问题）" class="headerlink" title="Sqoop 数据导出 Parquet（项目中遇到的问题）"></a>Sqoop 数据导出 Parquet（项目中遇到的问题）</h2><p>ADS 层数据用 Sqoop 往 MySQL 中导入数据的时候，如果用了 ORC（Parquet）不能导入，需转化成 text 格式。</p><ol><li>创建临时表，把 Parquet 中表数据导入到临时表，把临时表导出到目标表用于可视化；</li><li>Sqoop 里面有参数，可以直接把 Parquet 转换为 text；</li><li>ADS 层建表的时候就不要建 Parquet 表。</li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Sqoop/">Sqoop</category>
      
      
      <comments>https://blog.eurkon.com/post/420614eb.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Hive SQL 大全</title>
      <link>https://blog.eurkon.com/post/7891b2e6.html</link>
      <guid>https://blog.eurkon.com/post/7891b2e6.html</guid>
      <pubDate>Wed, 07 Apr 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文基本涵盖了 Hive 日常使用的所有 SQL，因为 SQL 太多，所以将 SQL 进行了如下分类：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文基本涵盖了 Hive 日常使用的所有 SQL，因为 SQL 太多，所以将 SQL 进行了如下分类：</p><ol><li><p><a href="#Hive-的-DDL-语法">DDL 语句</a>（数据定义语句）：</p><ol><li><a href="#对数据库的操作">对数据库的操作</a>：包含创建、修改数据库。</li><li><a href="#对数据表的操作">对数据表的操作</a>：分为内部表及外部表，分区表和分桶表。</li></ol></li><li><p><a href="#Hive-的-DQL-语法">DQL 语句</a>（数据查询语句）：</p><ol><li><a href="#单表查询、关联查询">单表查询、关联查询</a>。</li><li><a href="#Hive-函数">Hive 函数</a>：包含聚合函数，条件函数，日期函数，字符串函数等。</li><li><a href="#行转列及列转行">行转列及列转行</a>：lateral view 与 explode 以及 reflect。</li><li><a href="#窗口函数与分析函数">窗口函数与分析函数</a>。</li><li><a href="#其他一些窗口函数">其他一些窗口函数</a>。</li></ol></li></ol><h2 id="Hive-的-DDL-语法"><a href="#Hive-的-DDL-语法" class="headerlink" title="Hive 的 DDL 语法"></a>Hive 的 DDL 语法</h2><h3 id="对数据库的操作"><a href="#对数据库的操作" class="headerlink" title="对数据库的操作"></a>对数据库的操作</h3><h4 id="创建数据库"><a href="#创建数据库" class="headerlink" title="创建数据库"></a>创建数据库</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">database</span> <span class="keyword">if</span> <span class="keyword">not</span> <span class="keyword">exists</span> myhive;</span><br><span class="line"><span class="comment"># 说明：hive 的表存放位置模式是由 hive-site.xml 当中的一个属性指定的：hive.metastore.warehouse.dir</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建数据库并指定 HDFS 存储位置 :</span></span><br><span class="line"><span class="keyword">create</span> <span class="keyword">database</span> myhive2 location <span class="string">&#x27;/myhive2&#x27;</span>;</span><br></pre></td></tr></table></figure></p><h4 id="修改数据库"><a href="#修改数据库" class="headerlink" title="修改数据库"></a>修改数据库</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter</span> <span class="keyword">database</span> myhive2 <span class="keyword">set</span> dbproperties(<span class="string">&#x27;createtime&#x27;</span>=<span class="string">&#x27;20210329&#x27;</span>);</span><br></pre></td></tr></table></figure></p><p><strong>说明：</strong>可以使用 <code>alter database</code> 命令来修改数据库的一些属性。但是数据库的元数据信息是不可更改的，包括数据库的名称以及数据库所在的位置</p><h4 id="查看数据库信息"><a href="#查看数据库信息" class="headerlink" title="查看数据库信息"></a>查看数据库信息</h4><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看数据库基本信息</span></span><br><span class="line">hive (myhive)&gt; desc database myhive2;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看数据库更多详细信息</span></span><br><span class="line">hive (myhive)&gt; desc database extended myhive2;</span><br></pre></td></tr></table></figure></p><h4 id="删除数据库"><a href="#删除数据库" class="headerlink" title="删除数据库"></a>删除数据库</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 删除一个空数据库，如果数据库下面有数据表，那么就会报错</span></span><br><span class="line"><span class="keyword">drop</span> <span class="keyword">database</span> myhive2;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 强制删除数据库，包含数据库下面的表一起删除</span></span><br><span class="line"><span class="keyword">drop</span> <span class="keyword">database</span> myhive <span class="keyword">cascade</span>;</span><br></pre></td></tr></table></figure></p><h3 id="对数据表的操作"><a href="#对数据表的操作" class="headerlink" title="对数据表的操作"></a>对数据表的操作</h3><h4 id="对管理表（内部表）的操作"><a href="#对管理表（内部表）的操作" class="headerlink" title="对管理表（内部表）的操作"></a>对管理表（内部表）的操作</h4><h5 id="建内部表"><a href="#建内部表" class="headerlink" title="建内部表"></a>建内部表</h5><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">hive (myhive)&gt; use myhive; -- 使用 myhive 数据库</span><br><span class="line">hive (myhive)&gt; create table stu(id int,name string);</span><br><span class="line">hive (myhive)&gt; insert into stu values (1,&quot;zhangsan&quot;);</span><br><span class="line">hive (myhive)&gt; insert into stu values (1,&quot;zhangsan&quot;), (2,&quot;lisi&quot;); -- 一次插入多条数据</span><br><span class="line">hive (myhive)&gt; select * from stu;</span><br></pre></td></tr></table></figure></p><h5 id="Hive-建表时候的字段类型"><a href="#Hive-建表时候的字段类型" class="headerlink" title="Hive 建表时候的字段类型:"></a>Hive 建表时候的字段类型:</h5><p><strong>原始类型</strong></p><div class="table-container"><table><thead><tr><th>类型</th><th>描述</th><th>字面量示例</th></tr></thead><tbody><tr><td>BOOLEAN</td><td>true/false</td><td>TRUE</td></tr><tr><td>TINYINT</td><td>1 字节的有符号整数，-128~127</td><td>1Y</td></tr><tr><td>SMALLINT</td><td>2 个字节的有符号整数，-32768~32767</td><td>1S</td></tr><tr><td>INT</td><td>4 个字节的带符号整数</td><td>1</td></tr><tr><td>BIGINT</td><td>8 字节带符号整数</td><td>1L</td></tr><tr><td>FLOAT</td><td>4 字节单精度浮点数</td><td>1.0</td></tr><tr><td>DOUBLE</td><td>8 字节双精度浮点数</td><td>1.0</td></tr><tr><td>DEICIMAL</td><td>任意精度的带符号小数</td><td>1.0</td></tr><tr><td>STRING</td><td>字符串，变长</td><td>&quot;a&quot;, &#39;b&#39;</td></tr><tr><td>VARCHAR</td><td>变长字符串</td><td>&quot;a&quot;, &#39;b&#39;</td></tr><tr><td>CHAR</td><td>固定长度字符串</td><td>&quot;a&quot;, &#39;b&#39;</td></tr><tr><td>BINARY</td><td>字节数组</td><td>无法表示</td></tr><tr><td>TIMESTAMP</td><td>时间戳，毫秒值精度</td><td>162327493795</td></tr><tr><td>DATE</td><td>日期</td><td>‘2021-03-29&#39;</td></tr><tr><td>INTERVAL</td><td>时间频率间隔</td><td>&nbsp;</td></tr></tbody></table></div><p><strong>复杂类型</strong></p><div class="table-container"><table><thead><tr><th>类型</th><th>描述</th><th>字面量示例</th></tr></thead><tbody><tr><td>ARRAY</td><td>有序的同类型的集合</td><td>array(1, 2)</td></tr><tr><td>MAP</td><td>key-value，key 必须为原始类型，value 可以任意类型</td><td>map(&#39;a&#39;, 1, &#39;b&#39;, 2)</td></tr><tr><td>STRUCT</td><td>字段集合，类型可以不同</td><td>struct(&#39;1&#39;, 1, 1.0), named_struct(&#39;col1&#39;, &#39;1&#39;, &#39;col2&#39;, 1, &#39;clo3&#39;, 1.0)</td></tr><tr><td>UNION</td><td>在有限取值范围内的一个值</td><td>create_union(1, &#39;a&#39;, 63)</td></tr></tbody></table></div><p>对 decimal 类型简单解释下：</p><p>用法：<code>decimal(11, 2)</code> 代表最多有 11 位数字，其中后 2 位是小数，整数部分是 9 位；如果整数部分超过 9 位，则这个字段就会变成 <code>NULL</code>；如果小数部分不足 2 位，则后面用 0 补齐两位，如果小数部分超过两位，则超出部分四舍五入。</p><p>也可直接写 <code>decimal</code>，后面不指定位数，默认是 <code>decimal(10, 0)</code>，整数 10 位，没有小数。</p><h5 id="创建表并指定字段之间的分隔符"><a href="#创建表并指定字段之间的分隔符" class="headerlink" title="创建表并指定字段之间的分隔符"></a>创建表并指定字段之间的分隔符</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> <span class="keyword">if</span> <span class="keyword">not</span> <span class="keyword">exists</span> stu2(<span class="keyword">id</span> <span class="built_in">int</span> ,<span class="keyword">name</span> <span class="keyword">string</span>) <span class="keyword">row</span> <span class="keyword">format</span> <span class="keyword">delimited</span> <span class="keyword">fields</span> <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&#x27;\t&#x27;</span> <span class="keyword">stored</span> <span class="keyword">as</span> textfile location <span class="string">&#x27;/user/stu2&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># row format delimited fields terminated by &#x27;\t&#x27; 指定字段分隔符，默认分隔符为 &#x27;\001&#x27;</span></span><br><span class="line"><span class="comment"># stored as 指定存储格式</span></span><br><span class="line"><span class="comment"># location 指定存储位置</span></span><br></pre></td></tr></table></figure></p><h5 id="根据查询结果创建表"><a href="#根据查询结果创建表" class="headerlink" title="根据查询结果创建表"></a>根据查询结果创建表</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> stu3 <span class="keyword">as</span> <span class="keyword">select</span> * <span class="keyword">from</span> stu2;</span><br></pre></td></tr></table></figure></p><h5 id="根据已经存在的表结构创建表"><a href="#根据已经存在的表结构创建表" class="headerlink" title="根据已经存在的表结构创建表"></a>根据已经存在的表结构创建表</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> stu4 <span class="keyword">like</span> stu2;</span><br></pre></td></tr></table></figure></p><h5 id="查询表的结构"><a href="#查询表的结构" class="headerlink" title="查询表的结构"></a>查询表的结构</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 只查询表内字段及属性</span></span><br><span class="line">desc stu2;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 详细查询</span></span><br><span class="line">desc formatted stu2;</span><br></pre></td></tr></table></figure></p><h5 id="查询创建表的语句"><a href="#查询创建表的语句" class="headerlink" title="查询创建表的语句"></a>查询创建表的语句</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> <span class="keyword">create</span> <span class="keyword">table</span> stu2;</span><br></pre></td></tr></table></figure></p><h4 id="对外部表操作"><a href="#对外部表操作" class="headerlink" title="对外部表操作"></a>对外部表操作</h4><p>外部表因为是指定其他的 HDFS 路径的数据加载到表当中来，所以 Hive 表会认为自己不完全独占这份数据，所以删除 Hive 表的时候，数据仍然存放在 HDFS 当中，不会删掉，只会删除表的元数据。</p><h5 id="构建外部表"><a href="#构建外部表" class="headerlink" title="构建外部表"></a>构建外部表</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">external</span> <span class="keyword">table</span> student (s_id <span class="keyword">string</span>,s_name <span class="keyword">string</span>) <span class="keyword">row</span> <span class="keyword">format</span> <span class="keyword">delimited</span> <span class="keyword">fields</span> <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&#x27;\t&#x27;</span>;</span><br></pre></td></tr></table></figure></p><h5 id="从本地文件系统向表中加载数据"><a href="#从本地文件系统向表中加载数据" class="headerlink" title="从本地文件系统向表中加载数据"></a>从本地文件系统向表中加载数据</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 追加操作</span></span><br><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&#x27;/export/servers/hivedatas/student.csv&#x27;</span> <span class="keyword">into</span> <span class="keyword">table</span> student;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 覆盖操作</span></span><br><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&#x27;/export/servers/hivedatas/student.csv&#x27;</span> overwrite <span class="keyword">into</span> <span class="keyword">table</span> student;</span><br></pre></td></tr></table></figure></p><h5 id="从-HDFS-文件系统向表中加载数据"><a href="#从-HDFS-文件系统向表中加载数据" class="headerlink" title="从 HDFS 文件系统向表中加载数据"></a>从 HDFS 文件系统向表中加载数据</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> inpath <span class="string">&#x27;/hivedatas/techer.csv&#x27;</span> <span class="keyword">into</span> <span class="keyword">table</span> techer;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载数据到指定分区</span></span><br><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> inpath <span class="string">&#x27;/hivedatas/techer.csv&#x27;</span> <span class="keyword">into</span> <span class="keyword">table</span> techer <span class="keyword">partition</span>(cur_date=<span class="number">20201210</span>);</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>、</p><ol><li>使用 <code>load data local</code> 表示从本地文件系统加载，文件会拷贝到 HDFS 上。</li><li>使用 <code>load data</code> 表示从 HDFS 文件系统加载，文件会直接移动到 Hive 相关目录下，注意不是拷贝过去，因为 Hive 认为 HDFS 文件已经有 3 副本了，没必要再次拷贝了。</li><li>如果表是分区表，<code>load</code> 时不指定分区会报错。</li><li>如果加载相同文件名的文件，会被自动重命名。</li></ol><h4 id="对分区表的操作"><a href="#对分区表的操作" class="headerlink" title="对分区表的操作"></a>对分区表的操作</h4><h5 id="创建分区表的语法"><a href="#创建分区表的语法" class="headerlink" title="创建分区表的语法"></a>创建分区表的语法</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> score(s_id <span class="keyword">string</span>, s_score <span class="built_in">int</span>) partitioned <span class="keyword">by</span> (<span class="keyword">month</span> <span class="keyword">string</span>);</span><br></pre></td></tr></table></figure></p><h5 id="创建一个表带多个分区"><a href="#创建一个表带多个分区" class="headerlink" title="创建一个表带多个分区"></a>创建一个表带多个分区</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> score2 (s_id <span class="keyword">string</span>, s_score <span class="built_in">int</span>) partitioned <span class="keyword">by</span> (<span class="keyword">year</span> <span class="keyword">string</span>,<span class="keyword">month</span> <span class="keyword">string</span>,<span class="keyword">day</span> <span class="keyword">string</span>);</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>Hive 表创建的时候可以用 <code>location</code> 指定一个文件或者文件夹，当指定文件夹时，Hive 会加载文件夹下的所有文件，当表中无分区时，这个文件夹下不能再有文件夹，否则报错。</p><p>当表是分区表时，比如 <code>partitioned by (day string)</code>，则这个文件夹下的每一个文件夹就是一个分区，且文件夹名为 <code>day=20201123</code> 这种格式，然后使用：<code>msck repair table score;</code> 修复表结构，成功之后即可看到数据已经全部加载到表当中去了</p><h5 id="加载数据到一个分区的表中"><a href="#加载数据到一个分区的表中" class="headerlink" title="加载数据到一个分区的表中"></a>加载数据到一个分区的表中</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&#x27;/export/servers/hivedatas/score.csv&#x27;</span> <span class="keyword">into</span> <span class="keyword">table</span> score <span class="keyword">partition</span> (<span class="keyword">month</span>=<span class="string">&#x27;201806&#x27;</span>);</span><br></pre></td></tr></table></figure></p><h5 id="加载数据到一个多分区的表中去"><a href="#加载数据到一个多分区的表中去" class="headerlink" title="加载数据到一个多分区的表中去"></a>加载数据到一个多分区的表中去</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&#x27;/export/servers/hivedatas/score.csv&#x27;</span> <span class="keyword">into</span> <span class="keyword">table</span> score2 <span class="keyword">partition</span>(<span class="keyword">year</span>=<span class="string">&#x27;2018&#x27;</span>,<span class="keyword">month</span>=<span class="string">&#x27;06&#x27;</span>,<span class="keyword">day</span>=<span class="string">&#x27;01&#x27;</span>);</span><br></pre></td></tr></table></figure></p><h5 id="查看分区"><a href="#查看分区" class="headerlink" title="查看分区"></a>查看分区</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> <span class="keyword">partitions</span> score;</span><br></pre></td></tr></table></figure></p><h5 id="添加一个分区"><a href="#添加一个分区" class="headerlink" title="添加一个分区"></a>添加一个分区</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> score <span class="keyword">add</span> <span class="keyword">partition</span>(<span class="keyword">month</span>=<span class="string">&#x27;201805&#x27;</span>);</span><br></pre></td></tr></table></figure></p><h5 id="同时添加多个分区"><a href="#同时添加多个分区" class="headerlink" title="同时添加多个分区"></a>同时添加多个分区</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> score <span class="keyword">add</span> <span class="keyword">partition</span>(<span class="keyword">month</span>=<span class="string">&#x27;201804&#x27;</span>) <span class="keyword">partition</span>(<span class="keyword">month</span> = <span class="string">&#x27;201803&#x27;</span>);</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>添加分区之后就可以在 HDFS 文件系统当中看到表下面多了一个文件夹。</p><h5 id="删除分区"><a href="#删除分区" class="headerlink" title="删除分区"></a>删除分区</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> score <span class="keyword">drop</span> <span class="keyword">partition</span>(<span class="keyword">month</span> = <span class="string">&#x27;201806&#x27;</span>);</span><br></pre></td></tr></table></figure></p><h4 id="对分桶表操作"><a href="#对分桶表操作" class="headerlink" title="对分桶表操作"></a>对分桶表操作</h4><p>将数据按照指定的字段进行分成多个桶中去，就是按照分桶字段进行哈希划分到多个文件当中去分区就是分文件夹，分桶就是分文件。</p><p>分桶优点：</p><ul><li>提高 join 查询效率</li><li>提高抽样效率</li></ul><h5 id="开启-Hive-的捅表功能"><a href="#开启-Hive-的捅表功能" class="headerlink" title="开启 Hive 的捅表功能"></a>开启 Hive 的捅表功能</h5><p><code>set hive.enforce.bucketing=true;</code></p><h5 id="设置-Reduce-的个数"><a href="#设置-Reduce-的个数" class="headerlink" title="设置 Reduce 的个数"></a>设置 Reduce 的个数</h5><p><code>set mapreduce.job.reduces=3;</code></p><h5 id="创建桶表"><a href="#创建桶表" class="headerlink" title="创建桶表"></a>创建桶表</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> course (c_id <span class="keyword">string</span>,c_name <span class="keyword">string</span>) clustered <span class="keyword">by</span>(c_id) <span class="keyword">into</span> <span class="number">3</span> buckets;</span><br></pre></td></tr></table></figure></p><p>桶表的数据加载：由于桶表的数据加载通过 <code>hdfs dfs -put</code> 文件或者通过 <code>load data</code> 均不可以，只能通过 <code>insert overwrite</code> 进行加载。</p><p>所以把文件加载到桶表中，需要先创建普通表，并通过 <code>insert overwrite</code> 的方式将普通表的数据通过查询的方式加载到桶表当中去。</p><h5 id="通过-insert-overwrite-给桶表中加载数据"><a href="#通过-insert-overwrite-给桶表中加载数据" class="headerlink" title="通过 insert overwrite 给桶表中加载数据"></a>通过 insert overwrite 给桶表中加载数据</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">insert</span> overwrite <span class="keyword">table</span> course <span class="keyword">select</span> * <span class="keyword">from</span> course_common cluster <span class="keyword">by</span>(c_id); <span class="comment">-- 最后指定桶字段</span></span><br></pre></td></tr></table></figure></p><h4 id="修改表和删除表"><a href="#修改表和删除表" class="headerlink" title="修改表和删除表"></a>修改表和删除表</h4><h5 id="修改表名称"><a href="#修改表名称" class="headerlink" title="修改表名称"></a>修改表名称</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> old_table_name <span class="keyword">rename</span> <span class="keyword">to</span> new_table_name;</span><br></pre></td></tr></table></figure></p><h5 id="增加-修改列信息"><a href="#增加-修改列信息" class="headerlink" title="增加/修改列信息"></a>增加/修改列信息</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查询表结构</span></span><br><span class="line">desc score5;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加列</span></span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> score5 <span class="keyword">add</span> <span class="keyword">columns</span> (mycol <span class="keyword">string</span>, mysco <span class="keyword">string</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment"># 更新列</span></span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> score5 <span class="keyword">change</span> <span class="keyword">column</span> mysco mysconew <span class="built_in">int</span>;</span><br></pre></td></tr></table></figure></p><h5 id="删除表操作"><a href="#删除表操作" class="headerlink" title="删除表操作"></a>删除表操作</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">drop</span> <span class="keyword">table</span> score5;</span><br></pre></td></tr></table></figure></p><h5 id="清空表操作"><a href="#清空表操作" class="headerlink" title="清空表操作"></a>清空表操作</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">truncate</span> <span class="keyword">table</span> score6;</span><br><span class="line"><span class="comment"># 只能清空管理表，也就是内部表；清空外部表，会产生错误</span></span><br></pre></td></tr></table></figure></p><p><strong>注意：truncate 和 drop：</strong></p><p>如果 HDFS 开启了回收站，<code>drop</code> 删除的表数据是可以从回收站恢复的，表结构恢复不了，需要自己重新创建；<code>truncate</code> 清空的表是不进回收站的，所以无法恢复 <code>truncate</code> 清空的表所以 <code>truncate</code> 一定慎用，一旦清空将无力回天。</p><h4 id="向-Hive-表中加载数据"><a href="#向-Hive-表中加载数据" class="headerlink" title="向 Hive 表中加载数据"></a>向 Hive 表中加载数据</h4><h5 id="直接向分区表中插入数据"><a href="#直接向分区表中插入数据" class="headerlink" title="直接向分区表中插入数据"></a>直接向分区表中插入数据</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">insert</span> <span class="keyword">into</span> <span class="keyword">table</span> score <span class="keyword">partition</span>(<span class="keyword">month</span> =<span class="string">&#x27;201807&#x27;</span>) <span class="keyword">values</span> (<span class="string">&#x27;001&#x27;</span>,<span class="string">&#x27;002&#x27;</span>,<span class="string">&#x27;100&#x27;</span>);</span><br></pre></td></tr></table></figure></p><h5 id="通过-load-方式加载数据"><a href="#通过-load-方式加载数据" class="headerlink" title="通过 load 方式加载数据"></a>通过 load 方式加载数据</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&#x27;/export/servers/hivedatas/score.csv&#x27;</span> overwrite <span class="keyword">into</span> <span class="keyword">table</span> score <span class="keyword">partition</span>(<span class="keyword">month</span>=<span class="string">&#x27;201806&#x27;</span>);</span><br></pre></td></tr></table></figure></p><h5 id="通过查询方式加载数据"><a href="#通过查询方式加载数据" class="headerlink" title="通过查询方式加载数据"></a>通过查询方式加载数据</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">insert</span> overwrite <span class="keyword">table</span> score2 <span class="keyword">partition</span>(<span class="keyword">month</span> = <span class="string">&#x27;201806&#x27;</span>) <span class="keyword">select</span> s_id,c_id,s_score <span class="keyword">from</span> score1;</span><br></pre></td></tr></table></figure></p><h5 id="查询语句中创建表并加载数据"><a href="#查询语句中创建表并加载数据" class="headerlink" title="查询语句中创建表并加载数据"></a>查询语句中创建表并加载数据</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> score2 <span class="keyword">as</span> <span class="keyword">select</span> * <span class="keyword">from</span> score1;</span><br></pre></td></tr></table></figure></p><h5 id="在创建表是通过-location-指定加载数据的路径"><a href="#在创建表是通过-location-指定加载数据的路径" class="headerlink" title="在创建表是通过 location 指定加载数据的路径"></a>在创建表是通过 location 指定加载数据的路径</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">external</span> <span class="keyword">table</span> score6 (s_id <span class="keyword">string</span>,c_id <span class="keyword">string</span>,s_score <span class="built_in">int</span>) <span class="keyword">row</span> <span class="keyword">format</span> <span class="keyword">delimited</span> <span class="keyword">fields</span> <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&#x27;,&#x27;</span> location <span class="string">&#x27;/myscore&#x27;</span>;</span><br></pre></td></tr></table></figure></p><h5 id="export-导出与-import-导入-Hive-表数据（内部表操作）"><a href="#export-导出与-import-导入-Hive-表数据（内部表操作）" class="headerlink" title="export 导出与 import 导入 Hive 表数据（内部表操作）"></a>export 导出与 import 导入 Hive 表数据（内部表操作）</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> techer2 <span class="keyword">like</span> techer; <span class="comment">--依据已有表结构创建表</span></span><br><span class="line"></span><br><span class="line">export table techer to &#x27;/export/techer&#x27;;</span><br><span class="line"></span><br><span class="line">import table techer2 from &#x27;/export/techer&#x27;;</span><br></pre></td></tr></table></figure></p><h4 id="Hive-表中数据导出"><a href="#Hive-表中数据导出" class="headerlink" title="Hive 表中数据导出"></a>Hive 表中数据导出</h4><h5 id="insert-导出"><a href="#insert-导出" class="headerlink" title="insert 导出"></a>insert 导出</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将查询的结果导出到本地</span></span><br><span class="line"><span class="keyword">insert</span> overwrite <span class="keyword">local</span> <span class="keyword">directory</span> <span class="string">&#x27;/export/servers/exporthive&#x27;</span> <span class="keyword">select</span> * <span class="keyword">from</span> score;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将查询的结果格式化导出到本地</span></span><br><span class="line"><span class="keyword">insert</span> overwrite <span class="keyword">local</span> <span class="keyword">directory</span> <span class="string">&#x27;/export/servers/exporthive&#x27;</span> <span class="keyword">row</span> <span class="keyword">format</span> <span class="keyword">delimited</span> <span class="keyword">fields</span> <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&#x27;\t&#x27;</span> collection items <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&#x27;#&#x27;</span> <span class="keyword">select</span> * <span class="keyword">from</span> student;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将查询的结果导出到 HDFS 上（没有 local）</span></span><br><span class="line"><span class="keyword">insert</span> overwrite <span class="keyword">directory</span> <span class="string">&#x27;/export/servers/exporthive&#x27;</span> <span class="keyword">row</span> <span class="keyword">format</span> <span class="keyword">delimited</span> <span class="keyword">fields</span> <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&#x27;\t&#x27;</span> collection items <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&#x27;#&#x27;</span> <span class="keyword">select</span> * <span class="keyword">from</span> score;</span><br></pre></td></tr></table></figure></p><h5 id="Hadoop-命令导出到本地"><a href="#Hadoop-命令导出到本地" class="headerlink" title="Hadoop 命令导出到本地"></a>Hadoop 命令导出到本地</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dfs -get /export/servers/exporthive/000000_0 /export/servers/exporthive/local.txt;</span><br></pre></td></tr></table></figure></p><h5 id="hive-shell-命令导出"><a href="#hive-shell-命令导出" class="headerlink" title="hive shell 命令导出"></a>hive shell 命令导出</h5><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 基本语法：hive -f/-e 执行语句或者脚本 &gt; file</span></span><br><span class="line"></span><br><span class="line">hive -e &quot;select * from myhive.score;&quot; &gt; /export/servers/exporthive/score.txt</span><br><span class="line"></span><br><span class="line">hive -f export.sh &gt; /export/servers/exporthive/score.txt</span><br></pre></td></tr></table></figure></p><h5 id="export-导出到-HDFS-上"><a href="#export-导出到-HDFS-上" class="headerlink" title="export 导出到 HDFS 上"></a>export 导出到 HDFS 上</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">export table score to &#x27;/export/exporthive/score&#x27;;</span><br></pre></td></tr></table></figure></p><h2 id="Hive-的-DQL-语法"><a href="#Hive-的-DQL-语法" class="headerlink" title="Hive 的 DQL 语法"></a>Hive 的 DQL 语法</h2><h3 id="单表查询、关联查询"><a href="#单表查询、关联查询" class="headerlink" title="单表查询、关联查询"></a>单表查询、关联查询</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> [<span class="keyword">ALL</span> | <span class="keyword">DISTINCT</span>] select_expr, select_expr, ... </span><br><span class="line"><span class="keyword">FROM</span> table_reference</span><br><span class="line">[<span class="keyword">WHERE</span> where_condition] </span><br><span class="line">[<span class="keyword">GROUP</span> <span class="keyword">BY</span> col_list [<span class="keyword">HAVING</span> condition]] </span><br><span class="line">[CLUSTER <span class="keyword">BY</span> col_list | [<span class="keyword">DISTRIBUTE</span> <span class="keyword">BY</span> col_list] [<span class="keyword">SORT</span> <span class="keyword">BY</span>| <span class="keyword">ORDER</span> <span class="keyword">BY</span> col_list]] </span><br><span class="line">[<span class="keyword">LIMIT</span> <span class="built_in">number</span>]</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong></p><ol><li><code>order by</code> 会对输入做全局排序，因此只有一个 reducer，会导致当输入规模较大时，需要较长的计算时间。</li><li><code>sort by</code> 不是全局排序，其在数据进入 reducer 前完成排序。因此，如果用 <code>sort by</code> 进行排序，并且设置 <code>mapred.reduce.tasks&gt;1</code>，则 <code>sort by</code> 只保证每个 reducer 的输出有序，不保证全局有序。</li><li><code>distribute by(字段)</code> 根据指定的字段将数据分到不同的 reducer，且分发算法是 hash 散列。</li><li><code>cluster by(字段)</code> 除了具有 <code>distribute by</code> 的功能外，还会对该字段进行排序。</li></ol><p>因此，如果分桶和 <code>sort</code> 字段是同一个时，此时，<code>cluster by = distribute by + sort by</code>。</p><h4 id="WHERE-语句"><a href="#WHERE-语句" class="headerlink" title="WHERE 语句"></a>WHERE 语句</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> score <span class="keyword">where</span> s_score &lt; <span class="number">60</span>;</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>小于某个值是不包含 NULL 的，如上查询结果是把 s_score 为 NULL 的行剔除的。</p><h4 id="GROUP-BY-分组"><a href="#GROUP-BY-分组" class="headerlink" title="GROUP BY 分组"></a>GROUP BY 分组</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> s_id ,<span class="keyword">avg</span>(s_score) <span class="keyword">from</span> score <span class="keyword">group</span> <span class="keyword">by</span> s_id;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 分组后对数据进行筛选，使用 having</span></span><br><span class="line"><span class="keyword">select</span> s_id ,<span class="keyword">avg</span>(s_score) avgscore <span class="keyword">from</span> score <span class="keyword">group</span> <span class="keyword">by</span> s_id <span class="keyword">having</span> avgscore &gt; <span class="number">85</span>;</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong></p><p>如果使用 <code>group by</code> 分组，则 <code>select</code> 后面只能写分组的字段或者聚合函数。<code>where</code> 和 <code>having</code> 区别：</p><ul><li><code>having</code> 是在 <code>group by</code> 分完组之后再对数据进行筛选，所以 <code>having</code> 要筛选的字段只能是分组字段或者聚合函数</li><li><code>where</code> 是从数据表中的字段直接进行的筛选的，所以不能跟在 <code>group by</code> 后面，也不能使用聚合函数</li></ul><h4 id="JOIN-连接"><a href="#JOIN-连接" class="headerlink" title="JOIN 连接"></a>JOIN 连接</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># INNER JOIN 内连接：只有进行连接的两个表中都存在与连接条件相匹配的数据才会被保留下来</span></span><br><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> techer t [<span class="keyword">inner</span>] <span class="keyword">join</span> course c <span class="keyword">on</span> t.t_id = c.t_id; <span class="comment">-- inner 可省略</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># LEFT OUTER JOIN 左外连接：左边所有数据会被返回，右边符合条件的被返回</span></span><br><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> techer t <span class="keyword">left</span> <span class="keyword">join</span> course c <span class="keyword">on</span> t.t_id = c.t_id; <span class="comment">-- outer 可省略</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># RIGHT OUTER JOIN 右外连接：右边所有数据会被返回，左边符合条件的被返回</span></span><br><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> techer t <span class="keyword">right</span> <span class="keyword">join</span> course c <span class="keyword">on</span> t.t_id = c.t_id;</span><br><span class="line"></span><br><span class="line"><span class="comment"># FULL OUTER JOIN 满外(全外)连接: 将会返回所有表中符合条件的所有记录。如果任一表的指定字段没有符合条件的值的话，那么就使用 NULL 值替代。</span></span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> techer t <span class="keyword">FULL</span> <span class="keyword">JOIN</span> course c <span class="keyword">ON</span> t.t_id = c.t_id;</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong></p><ol><li>Hive2 版本已经支持不等值连接，就是 <code>join on</code> 条件后面可以使用大于小于符号了；并且也支持 <code>join on</code> 条件后跟 <code>or</code>（早前版本 <code>on</code> 后只支持 <code>=</code> 和 <code>and</code>，不支持 <code>&gt;</code>、<code>&lt;</code> 和 <code>or</code>）；</li><li>如 Hive 执行引擎使用 MapReduce，一个 <code>join</code> 就会启动一个 job，一条 sql 语句中如有多个 <code>join</code>，则会启动多个 job。</li></ol><p><strong>注意：</strong>表之间用 <code>逗号(,)</code> 连接和 <code>inner join</code> 是一样的。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> table_a,table_b <span class="keyword">where</span> table_a.id = table_b.id;</span><br></pre></td></tr></table></figure></p><p>它们的执行效率没有区别，只是书写方式不同，用 <code>逗号(,)</code> 是 sql 89 标准，<code>join</code> 是 sql 92 标准。用 <code>逗号(,)</code> 连接后面过滤条件用 <code>where</code> ，用 <code>join</code> 连接后面过滤条件是 <code>on</code>。</p><h4 id="ORDER-BY-排序"><a href="#ORDER-BY-排序" class="headerlink" title="ORDER BY 排序"></a>ORDER BY 排序</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 全局排序，只会有一个 reduce</span></span><br><span class="line"><span class="comment"># ASC（ascend）: 升序（默认） DESC（descend）: 降序</span></span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> student s <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> score sco <span class="keyword">ON</span> s.s_id = sco.s_id <span class="keyword">ORDER</span> <span class="keyword">BY</span> sco.s_score <span class="keyword">DESC</span>;</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong><code>order by</code> 是全局排序，所以最后只有一个 reduce，也就是在一个节点执行，如果数据量太大，就会耗费较长时间。</p><h4 id="SORT-BY-局部排序"><a href="#SORT-BY-局部排序" class="headerlink" title="SORT BY 局部排序"></a>SORT BY 局部排序</h4><p>每个 MapReduce 内部进行排序，对全局结果集来说不是排序。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 设置 reduce 个数</span></span><br><span class="line"><span class="keyword">set</span> mapreduce.job.reduces=<span class="number">3</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看设置 reduce 个数</span></span><br><span class="line"><span class="keyword">set</span> mapreduce.job.reduces;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查询成绩按照成绩降序排列</span></span><br><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> score <span class="keyword">sort</span> <span class="keyword">by</span> s_score;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将查询结果导入到文件中（按照成绩降序排列）</span></span><br><span class="line"><span class="keyword">insert</span> overwrite <span class="keyword">local</span> <span class="keyword">directory</span> <span class="string">&#x27;/export/servers/hivedatas/sort&#x27;</span> <span class="keyword">select</span> * <span class="keyword">from</span> score <span class="keyword">sort</span> <span class="keyword">by</span> s_score;</span><br></pre></td></tr></table></figure></p><h4 id="DISTRIBUTE-BY-分区排序"><a href="#DISTRIBUTE-BY-分区排序" class="headerlink" title="DISTRIBUTE BY 分区排序"></a>DISTRIBUTE BY 分区排序</h4><p><code>distribute by</code> 类似 MR 中 partition，进行分区，结合 <code>sort by</code> 使用。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 设置 reduce 的个数，将我们对应的 s_id 划分到对应的 reduce 当中去</span></span><br><span class="line"><span class="keyword">set</span> mapreduce.job.reduces=<span class="number">7</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 通过 distribute by 进行数据的分区</span></span><br><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> score <span class="keyword">distribute</span> <span class="keyword">by</span> s_id <span class="keyword">sort</span> <span class="keyword">by</span> s_score;</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>Hive 要求 <code>distribute by</code> 语句要写在 <code>sort by</code> 语句之前。</p><h4 id="CLUSTER-BY-排序"><a href="#CLUSTER-BY-排序" class="headerlink" title="CLUSTER BY 排序"></a>CLUSTER BY 排序</h4><p>当 <code>distribute by</code> 和 <code>sort by</code> 字段相同时，可以使用 <code>cluster by</code> 方式。<code>cluster by</code> 除了具有 <code>distribute by</code> 的功能外还兼具 <code>sort by</code> 的功能。但是排序只能是正序排序，不能指定排序规则为 <code>ASC</code> 或者 <code>DESC</code>。</p><p>以下两种写法等价：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> score cluster <span class="keyword">by</span> s_id;</span><br><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> score <span class="keyword">distribute</span> <span class="keyword">by</span> s_id <span class="keyword">sort</span> <span class="keyword">by</span> s_id;</span><br></pre></td></tr></table></figure></p><h3 id="Hive-函数"><a href="#Hive-函数" class="headerlink" title="Hive 函数"></a>Hive 函数</h3><h4 id="聚合函数"><a href="#聚合函数" class="headerlink" title="聚合函数"></a>聚合函数</h4><p>Hive 支持 <code>count()</code>，<code>max()</code>，<code>min()</code>，<code>sum()</code>，<code>avg()</code> 等常用的聚合函数。</p><p><strong>注意：</strong></p><ul><li>聚合操作时要注意 NULL 值；</li><li><code>count(*)</code> 包含 NULL 值，统计所有行数；</li><li><code>count(id)</code> 不包含 NULL 值；</li><li><code>min</code> 求最小值是不包含 NULL，除非所有值都是 NULL；</li><li><code>avg</code> 求平均值也是不包含 NULL。</li></ul><h5 id="非空集合总体变量函数"><a href="#非空集合总体变量函数" class="headerlink" title="非空集合总体变量函数"></a>非空集合总体变量函数</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">语法：var_pop(col)</span><br><span class="line">返回值：double</span><br><span class="line">说明：统计结果集中 col 非空集合的总体变量（忽略 NULL）</span><br></pre></td></tr></table></figure></p><h5 id="非空集合样本变量函数"><a href="#非空集合样本变量函数" class="headerlink" title="非空集合样本变量函数"></a>非空集合样本变量函数</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">语法：var_samp(col)</span><br><span class="line">返回值：double</span><br><span class="line">说明：统计结果集中 col 非空集合的样本变量（忽略 NULL）</span><br></pre></td></tr></table></figure></p><h5 id="总体标准偏离函数"><a href="#总体标准偏离函数" class="headerlink" title="总体标准偏离函数"></a>总体标准偏离函数</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">语法：stddev_pop(col)</span><br><span class="line">返回值：double</span><br><span class="line">说明：该函数计算总体标准偏离，并返回总体变量的平方根，其返回值与 VAR_POP 函数的平方根相同</span><br></pre></td></tr></table></figure></p><h5 id="中位数函数"><a href="#中位数函数" class="headerlink" title="中位数函数"></a>中位数函数</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">语法：percentile(bigint col, p)</span><br><span class="line">返回值：double</span><br><span class="line">说明：求准确的第 p 个百分位数，p 必须介于 0 和 1 之间，但是 col 字段目前只支持整数，不支持浮点数类型</span><br></pre></td></tr></table></figure></p><h4 id="关系运算"><a href="#关系运算" class="headerlink" title="关系运算"></a>关系运算</h4><p>支持：等值(=)、不等值(!= 或 &lt;&gt;)、小于(&lt;)、小于等于(&lt;=)、大于(&gt;)、大于等于(&gt;=)、空值判断(IS NULL)、非空判断(IS NOT NULL)。</p><h5 id="LIKE-比较"><a href="#LIKE-比较" class="headerlink" title="LIKE 比较"></a>LIKE 比较</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">语法：A LIKE B</span><br><span class="line">操作类型: string</span><br><span class="line">说明：如果字符串 A 或者字符串 B 为 NULL，则返回 NULL；如果字符串 A 符合表达式 B 的正则语法，则为 TRUE；否则为 FALSE。B 中字符(_) 表示任意单个字符，而字符(%) 表示任意数量的字符。</span><br></pre></td></tr></table></figure></p><h5 id="RLIKE-操作"><a href="#RLIKE-操作" class="headerlink" title="RLIKE 操作"></a>RLIKE 操作</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">语法：A RLIKE B</span><br><span class="line">操作类型: string</span><br><span class="line">说明：如果字符串 A 或者字符串 B 为 NULL，则返回 NULL；如果字符串 A 符合 Java 正则表达式 B 的正则语法，则为 TRUE；否则为 FALSE。</span><br></pre></td></tr></table></figure></p><h5 id="REGEXP-操作"><a href="#REGEXP-操作" class="headerlink" title="REGEXP 操作"></a>REGEXP 操作</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：A REGEXP B</span><br><span class="line">操作类型: string</span><br><span class="line">说明：功能与 RLIKE 相同</span><br><span class="line">示例：<span class="keyword">select</span> <span class="number">1</span> <span class="keyword">from</span> tableName <span class="keyword">where</span> <span class="string">&#x27;footbar&#x27;</span> REGEXP <span class="string">&#x27;^f.*r$&#x27;</span>;</span><br><span class="line">结果：1</span><br></pre></td></tr></table></figure></p><h4 id="数学运算"><a href="#数学运算" class="headerlink" title="数学运算"></a>数学运算</h4><p>支持所有数值类型：加(+)、减(-)、乘(*)、除(/)、取余(%)、位与(&amp;)、位或(|)、位异或(^)、位取反(~)。</p><h4 id="逻辑运算"><a href="#逻辑运算" class="headerlink" title="逻辑运算"></a>逻辑运算</h4><p>支持：逻辑与(and)、逻辑或(or)、逻辑非(not)。</p><h4 id="数值运算"><a href="#数值运算" class="headerlink" title="数值运算"></a>数值运算</h4><h5 id="取整函数-round"><a href="#取整函数-round" class="headerlink" title="取整函数 round"></a>取整函数 round</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：round(double a)</span><br><span class="line">返回值：bigint</span><br><span class="line">说明：返回 double 类型的整数值部分（遵循四舍五入）</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">round</span>(<span class="number">3.1415926</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：3</span><br></pre></td></tr></table></figure></p><h5 id="指定精度取整函数-round"><a href="#指定精度取整函数-round" class="headerlink" title="指定精度取整函数 round"></a>指定精度取整函数 round</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：round(double a, int d)</span><br><span class="line">返回值：double</span><br><span class="line">说明：返回指定精度 d 的 double 类型</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">round</span>(<span class="number">3.1415926</span>,<span class="number">4</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：3.1416</span><br></pre></td></tr></table></figure></p><h5 id="向下取整函数-floor"><a href="#向下取整函数-floor" class="headerlink" title="向下取整函数 floor"></a>向下取整函数 floor</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：floor(double a)</span><br><span class="line">返回值：bigint</span><br><span class="line">说明：返回等于或者小于该 double 变量的最大的整数</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">floor</span>(<span class="number">3.641</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：3</span><br></pre></td></tr></table></figure></p><h5 id="向上取整函数-ceil"><a href="#向上取整函数-ceil" class="headerlink" title="向上取整函数 ceil"></a>向上取整函数 ceil</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：ceil(double a)</span><br><span class="line">返回值：bigint</span><br><span class="line">说明：返回等于或者大于该 double 变量的最小的整数</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">ceil</span>(<span class="number">3.1415926</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：4</span><br></pre></td></tr></table></figure></p><h5 id="取随机数函数-rand"><a href="#取随机数函数-rand" class="headerlink" title="取随机数函数 rand"></a>取随机数函数 rand</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：rand(), rand(int seed)</span><br><span class="line">返回值：double</span><br><span class="line">说明：返回一个 0 到 1 范围内的随机数。如果指定种子 seed，则会等到一个稳定的随机数序列</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">rand</span>() <span class="keyword">from</span> tableName; <span class="comment">-- 每次执行此语句得到的结果都不同</span></span><br><span class="line">结果：0.5577432776034763</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">rand</span>(<span class="number">100</span>); <span class="comment">-- 只要指定种子，每次执行此语句得到的结果一样的</span></span><br><span class="line">结果：0.7220096548596434</span><br></pre></td></tr></table></figure></p><h5 id="自然指数函数-exp"><a href="#自然指数函数-exp" class="headerlink" title="自然指数函数 exp"></a>自然指数函数 exp</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：exp(double a)</span><br><span class="line">返回值：double</span><br><span class="line">说明：返回自然对数 e 的 a 次方</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">exp</span>(<span class="number">2</span>);</span><br><span class="line">结果：7.38905609893065</span><br></pre></td></tr></table></figure></p><h5 id="以-10-为底对数函数-log10"><a href="#以-10-为底对数函数-log10" class="headerlink" title="以 10 为底对数函数 log10"></a>以 10 为底对数函数 log10</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">语法：log10(double a)</span><br><span class="line">返回值：double</span><br><span class="line">说明：返回以 10 为底的 a 的对数</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">log10</span>(<span class="number">100</span>);</span><br><span class="line">结果：2.0</span><br><span class="line">此外还有：以 2 为底对数函数：log2()、对数函数：log()</span><br></pre></td></tr></table></figure></p><h5 id="幂运算函数-pow"><a href="#幂运算函数-pow" class="headerlink" title="幂运算函数 pow"></a>幂运算函数 pow</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：pow(double a, double p)</span><br><span class="line">返回值：double</span><br><span class="line">说明：返回 a 的 p 次幂</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">pow</span>(<span class="number">2</span>,<span class="number">4</span>);</span><br><span class="line">结果：16.0</span><br></pre></td></tr></table></figure></p><h5 id="开平方函数-sqrt"><a href="#开平方函数-sqrt" class="headerlink" title="开平方函数 sqrt"></a>开平方函数 sqrt</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：sqrt(double a)</span><br><span class="line">返回值：double</span><br><span class="line">说明：返回 a 的平方根</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">sqrt</span>(<span class="number">16</span>);</span><br><span class="line">结果：4.0</span><br></pre></td></tr></table></figure></p><h5 id="二进制函数-bin"><a href="#二进制函数-bin" class="headerlink" title="二进制函数 bin"></a>二进制函数 bin</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：bin(bigint a)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回 a 的二进制代码表示</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">bin</span>(<span class="number">7</span>);</span><br><span class="line">结果：111</span><br><span class="line">十六进制函数：hex()、将十六进制转化为字符串函数：unhex()</span><br><span class="line">进制转换函数：conv(bigint num, int from_base, int to_base) 说明：将数值 num 从 from_base 进制转化到 to_base 进制</span><br></pre></td></tr></table></figure></p><p>此外还有很多数学函数：绝对值函数：<code>abs()</code>、正取余函数：<code>pmod()</code>、正弦函数：<code>sin()</code>、反正弦函数：<code>asin()</code>、余弦函数：<code>cos()</code>、反余弦函数：<code>acos()</code>、positive 函数：<code>positive()</code>、negative 函数：<code>negative()</code>。</p><h4 id="条件函数"><a href="#条件函数" class="headerlink" title="条件函数"></a>条件函数</h4><h5 id="IF-函数"><a href="#IF-函数" class="headerlink" title="IF 函数"></a>IF 函数</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：if(boolean testCondition, T valueTrue, T valueFalseOrNULL)</span><br><span class="line">返回值：T</span><br><span class="line">说明：当条件 testCondition 为 TRUE 时，返回 valueTrue；否则返回 valueFalseOrNULL</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">if</span>(<span class="number">1</span>=<span class="number">2</span>, <span class="number">100</span>, <span class="number">200</span>);</span><br><span class="line">结果：200</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">if</span>(<span class="number">1</span>=<span class="number">1</span>, <span class="number">100</span>, <span class="number">200</span>);</span><br><span class="line">结果：100</span><br></pre></td></tr></table></figure></p><h5 id="非空查找函数-coalesce"><a href="#非空查找函数-coalesce" class="headerlink" title="非空查找函数 coalesce"></a>非空查找函数 coalesce</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：coalesce(T v1, T v2, …)</span><br><span class="line">返回值：T</span><br><span class="line">说明：返回参数中的第一个非空值；如果所有值都为 NULL，那么返回 NULL</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">coalesce</span>(<span class="literal">NULL</span>, <span class="string">&#x27;100&#x27;</span>, <span class="string">&#x27;50&#x27;</span>);</span><br><span class="line">结果：100</span><br></pre></td></tr></table></figure></p><h5 id="条件判断函数-case-when"><a href="#条件判断函数-case-when" class="headerlink" title="条件判断函数 case when"></a>条件判断函数 case when</h5><p><strong>用法一</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：case when a then b [when c then d]* [else e] end</span><br><span class="line">返回值：T</span><br><span class="line">说明：如果 a 为 TRUE，则返回 b；如果 c 为 TRUE，则返回 d；否则返回 e。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">case</span> <span class="keyword">when</span> <span class="number">1</span>=<span class="number">2</span> <span class="keyword">then</span> <span class="string">&#x27;tom&#x27;</span> <span class="keyword">when</span> <span class="number">2</span>=<span class="number">2</span> <span class="keyword">then</span> <span class="string">&#x27;mary&#x27;</span> <span class="keyword">else</span> <span class="string">&#x27;tim&#x27;</span> <span class="keyword">end</span> <span class="keyword">from</span> tableName;</span><br><span class="line">结果：mary</span><br></pre></td></tr></table></figure></p><p><strong>用法二</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：case a when b then c [when d then e]* [else f] end</span><br><span class="line">返回值：T</span><br><span class="line">说明：如果 a 等于 b，那么返回 c；如果 a 等于 d，那么返回 e；否则返回 f。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">case</span> <span class="number">100</span> <span class="keyword">when</span> <span class="number">50</span> <span class="keyword">then</span> <span class="string">&#x27;tom&#x27;</span> <span class="keyword">when</span> <span class="number">100</span> <span class="keyword">then</span> <span class="string">&#x27;mary&#x27;</span> <span class="keyword">else</span> <span class="string">&#x27;tim&#x27;</span> <span class="keyword">end</span> <span class="keyword">from</span> tableName;</span><br><span class="line">结果：mary</span><br></pre></td></tr></table></figure></p><h4 id="日期函数"><a href="#日期函数" class="headerlink" title="日期函数"></a>日期函数</h4><p><strong>注意：</strong>以下 SQL 语句中的 <code>from tableName</code> 可去掉，不影响查询结果</p><h5 id="获取当前-UNIX-时间戳函数-unix-timestamp"><a href="#获取当前-UNIX-时间戳函数-unix-timestamp" class="headerlink" title="获取当前 UNIX 时间戳函数 unix_timestamp"></a>获取当前 UNIX 时间戳函数 unix_timestamp</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：unix_timestamp()</span><br><span class="line">返回值：bigint</span><br><span class="line">说明：获得当前时区的 UNIX 时间戳</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">unix_timestamp</span>() <span class="keyword">from</span> tableName;</span><br><span class="line">结果：1616906976</span><br></pre></td></tr></table></figure></p><h5 id="UNIX-时间戳转日期函数-from-unixtime"><a href="#UNIX-时间戳转日期函数-from-unixtime" class="headerlink" title="UNIX 时间戳转日期函数 from_unixtime"></a>UNIX 时间戳转日期函数 from_unixtime</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：from_unixtime(bigint unixtime[, string format])</span><br><span class="line">返回值：string</span><br><span class="line">说明：转化 UNIX 时间戳（从 1970-01-01 00:00:00 UTC 到指定时间的秒数）到当前时区的时间格式</span><br><span class="line">示例：<span class="keyword">select</span> from_unixtime(<span class="number">1616906976</span>, <span class="string">&#x27;yyyyMMdd&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：20210328</span><br></pre></td></tr></table></figure></p><h5 id="日期转-UNIX-时间戳函数-unix-timestamp"><a href="#日期转-UNIX-时间戳函数-unix-timestamp" class="headerlink" title="日期转 UNIX 时间戳函数 unix_timestamp"></a>日期转 UNIX 时间戳函数 unix_timestamp</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：unix_timestamp(string date)</span><br><span class="line">返回值：bigint</span><br><span class="line">说明：转换格式为 &quot;yyyy-MM-dd HH:mm:ss&quot; 的日期到 UNIX 时间戳。如果转化失败，则返回 NULL。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">unix_timestamp</span>(<span class="string">&#x27;2021-03-08 14:21:15&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：1615184475</span><br></pre></td></tr></table></figure></p><h5 id="指定格式日期转-UNIX-时间戳函数-unix-timestamp"><a href="#指定格式日期转-UNIX-时间戳函数-unix-timestamp" class="headerlink" title="指定格式日期转 UNIX 时间戳函数 unix_timestamp"></a>指定格式日期转 UNIX 时间戳函数 unix_timestamp</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：unix_timestamp(string date, string pattern)</span><br><span class="line">返回值：bigint</span><br><span class="line">说明：转换 pattern 格式的日期到 UNIX 时间戳。如果转化失败，则返回 NULL。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">unix_timestamp</span>(<span class="string">&#x27;2021-03-08 14:21:15&#x27;</span>, <span class="string">&#x27;yyyy-MM-dd HH:mm:ss&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：1615184475</span><br></pre></td></tr></table></figure></p><h5 id="日期时间转日期函数-to-date"><a href="#日期时间转日期函数-to-date" class="headerlink" title="日期时间转日期函数 to_date"></a>日期时间转日期函数 to_date</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：to_date(string timestamp)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回日期时间字段中的日期部分。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">to_date</span>(<span class="string">&#x27;2021-03-28 14:03:01&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：2021-03-28</span><br></pre></td></tr></table></figure></p><h5 id="日期转年函数-year"><a href="#日期转年函数-year" class="headerlink" title="日期转年函数 year"></a>日期转年函数 year</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：year(string date)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回日期中的年。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">year</span>(<span class="string">&#x27;2021-03-28 10:03:01&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：2021</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">year</span>(<span class="string">&#x27;2021-03-28&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：2021</span><br></pre></td></tr></table></figure></p><h5 id="日期转月函数-month"><a href="#日期转月函数-month" class="headerlink" title="日期转月函数 month"></a>日期转月函数 month</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：month (string date)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回日期中的月份。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">month</span>(<span class="string">&#x27;2020-12-28 12:03:01&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：12</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">month</span>(<span class="string">&#x27;2021-03-08&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：8</span><br></pre></td></tr></table></figure></p><h5 id="日期转天函数-day"><a href="#日期转天函数-day" class="headerlink" title="日期转天函数 day"></a>日期转天函数 day</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：day (string date)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回日期中的天。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">day</span>(<span class="string">&#x27;2020-12-08 10:03:01&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：8</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">day</span>(<span class="string">&#x27;2020-12-24&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：24</span><br></pre></td></tr></table></figure></p><h5 id="日期转小时函数-hour"><a href="#日期转小时函数-hour" class="headerlink" title="日期转小时函数 hour"></a>日期转小时函数 hour</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：hour (string date)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回日期中的小时。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">hour</span>(<span class="string">&#x27;2020-12-08 10:03:01&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：10</span><br></pre></td></tr></table></figure></p><h5 id="日期转分钟函数-minute"><a href="#日期转分钟函数-minute" class="headerlink" title="日期转分钟函数 minute"></a>日期转分钟函数 minute</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：minute (string date)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回日期中的分钟。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">minute</span>(<span class="string">&#x27;2020-12-08 10:03:01&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：3</span><br></pre></td></tr></table></figure></p><h5 id="日期转秒函数-second"><a href="#日期转秒函数-second" class="headerlink" title="日期转秒函数 second"></a>日期转秒函数 second</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：second (string date)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回日期中的秒。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">second</span>(<span class="string">&#x27;2020-12-08 10:03:01&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：1</span><br></pre></td></tr></table></figure></p><h5 id="日期转周函数-weekofyear"><a href="#日期转周函数-weekofyear" class="headerlink" title="日期转周函数 weekofyear"></a>日期转周函数 weekofyear</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：weekofyear (string date)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回日期在当前的周数。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">weekofyear</span>(<span class="string">&#x27;2020-12-08 10:03:01&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：49</span><br></pre></td></tr></table></figure></p><h5 id="日期比较函数-datediff"><a href="#日期比较函数-datediff" class="headerlink" title="日期比较函数 datediff"></a>日期比较函数 datediff</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：datediff(string enddate, string startdate)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回结束日期减去开始日期的天数。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">datediff</span>(<span class="string">&#x27;2020-12-08&#x27;</span>, <span class="string">&#x27;2012-05-09&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：213</span><br></pre></td></tr></table></figure></p><h5 id="日期增加函数-date-add"><a href="#日期增加函数-date-add" class="headerlink" title="日期增加函数 date_add"></a>日期增加函数 date_add</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：date_add(string startdate, int days)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回开始日期 startdate 增加 days 天后的日期。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">date_add</span>(<span class="string">&#x27;2020-12-08&#x27;</span>, <span class="number">10</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：2020-12-18</span><br></pre></td></tr></table></figure></p><h5 id="日期减少函数-date-sub"><a href="#日期减少函数-date-sub" class="headerlink" title="日期减少函数 date_sub"></a>日期减少函数 date_sub</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：date_sub (string startdate, int days)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回开始日期 startdate 减少 days 天后的日期。</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">date_sub</span>(<span class="string">&#x27;2020-12-08&#x27;</span>, <span class="number">10</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：2020-11-28</span><br></pre></td></tr></table></figure></p><h4 id="字符串函数"><a href="#字符串函数" class="headerlink" title="字符串函数"></a>字符串函数</h4><h5 id="字符串长度函数-length"><a href="#字符串长度函数-length" class="headerlink" title="字符串长度函数 length"></a>字符串长度函数 length</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：length(string A)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回字符串 A 的长度</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">length</span>(<span class="string">&#x27;abcdefg&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：7</span><br></pre></td></tr></table></figure></p><h5 id="字符串反转函数-reverse"><a href="#字符串反转函数-reverse" class="headerlink" title="字符串反转函数 reverse"></a>字符串反转函数 reverse</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：reverse(string A)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回字符串 A 的反转结果</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">reverse</span>(<span class="string">&#x27;abcdefg&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：gfedcba</span><br></pre></td></tr></table></figure></p><h5 id="字符串连接函数-concat"><a href="#字符串连接函数-concat" class="headerlink" title="字符串连接函数 concat"></a>字符串连接函数 concat</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：concat(string A, string B…)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回输入字符串连接后的结果，支持任意个输入字符串</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">concat</span>(<span class="string">&#x27;abc&#x27;</span>, <span class="string">&#x27;def&#x27;</span>, <span class="string">&#x27;gh&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：abcdefgh</span><br></pre></td></tr></table></figure></p><h5 id="带分隔符字符串连接函数-concat-ws"><a href="#带分隔符字符串连接函数-concat-ws" class="headerlink" title="带分隔符字符串连接函数 concat_ws"></a>带分隔符字符串连接函数 concat_ws</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：concat_ws(string SEP, string A, string B…)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回输入字符串连接后的结果，SEP 表示各个字符串间的分隔符</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">concat_ws</span>(<span class="string">&#x27;,&#x27;</span>, <span class="string">&#x27;abc&#x27;</span>, <span class="string">&#x27;def&#x27;</span>, <span class="string">&#x27;gh&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：abc,def,gh</span><br></pre></td></tr></table></figure></p><h5 id="字符串截取函数-substr-substring"><a href="#字符串截取函数-substr-substring" class="headerlink" title="字符串截取函数 substr, substring"></a>字符串截取函数 substr, substring</h5><p><strong>用法一</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">语法：substr(string A, int <span class="keyword">start</span>), <span class="keyword">substring</span>(<span class="keyword">string</span> A, <span class="built_in">int</span> <span class="keyword">start</span>)</span><br><span class="line">返回值：<span class="keyword">string</span></span><br><span class="line">说明：返回字符串 A 从 <span class="keyword">start</span> 位置到结尾的字符串</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">substr</span>(<span class="string">&#x27;abcde&#x27;</span>, <span class="number">3</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：cde</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">substring</span>(<span class="string">&#x27;abcde&#x27;</span>, <span class="number">3</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：cde</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">substr</span>(<span class="string">&#x27;abcde&#x27;</span>, <span class="number">-1</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：e</span><br></pre></td></tr></table></figure></p><p><strong>用法二</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">语法：substr(string A, int <span class="keyword">start</span>, <span class="built_in">int</span> <span class="keyword">len</span>), <span class="keyword">substring</span>(<span class="keyword">string</span> A, <span class="built_in">int</span> <span class="keyword">start</span>, <span class="built_in">int</span> <span class="keyword">len</span>)</span><br><span class="line">返回值：<span class="keyword">string</span></span><br><span class="line">说明：返回字符串 A 从 <span class="keyword">start</span> 位置开始，长度为 <span class="keyword">len</span> 的字符串</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">substr</span>(<span class="string">&#x27;abcde&#x27;</span>, <span class="number">3</span>, <span class="number">2</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：cd</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">substring</span>(<span class="string">&#x27;abcde&#x27;</span>, <span class="number">3</span>, <span class="number">2</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：cd</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">substring</span>(<span class="string">&#x27;abcde&#x27;</span>, <span class="number">-2</span>, <span class="number">2</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：de</span><br></pre></td></tr></table></figure></p><h5 id="字符串转大写函数-upper-ucase"><a href="#字符串转大写函数-upper-ucase" class="headerlink" title="字符串转大写函数 upper, ucase"></a>字符串转大写函数 upper, ucase</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：upper(string A) ucase(string A)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回字符串 A 的大写格式</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">upper</span>(<span class="string">&#x27;abSEd&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：ABSED</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">ucase</span>(<span class="string">&#x27;abSEd&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：ABSED</span><br></pre></td></tr></table></figure></p><h5 id="字符串转小写函数-lower-lcase"><a href="#字符串转小写函数-lower-lcase" class="headerlink" title="字符串转小写函数 lower, lcase"></a>字符串转小写函数 lower, lcase</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：lower(string A), lcase(string A)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回字符串 A 的小写格式</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">lower</span>(<span class="string">&#x27;abSEd&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：absed</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">lcase</span>(<span class="string">&#x27;abSEd&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：absed</span><br></pre></td></tr></table></figure></p><h5 id="去空格函数-trim"><a href="#去空格函数-trim" class="headerlink" title="去空格函数 trim"></a>去空格函数 trim</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：trim(string A)</span><br><span class="line">返回值：string</span><br><span class="line">说明：去除字符串两边的空格</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">trim</span>(<span class="string">&#x27; abc &#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：abc</span><br></pre></td></tr></table></figure></p><h5 id="左边去空格函数-ltrim"><a href="#左边去空格函数-ltrim" class="headerlink" title="左边去空格函数 ltrim"></a>左边去空格函数 ltrim</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：ltrim(string A)</span><br><span class="line">返回值：string</span><br><span class="line">说明：去除字符串左边的空格</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">ltrim</span>(<span class="string">&#x27; abc &#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：abc</span><br></pre></td></tr></table></figure></p><h5 id="右边去空格函数-rtrim"><a href="#右边去空格函数-rtrim" class="headerlink" title="右边去空格函数 rtrim"></a>右边去空格函数 rtrim</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：rtrim(string A)</span><br><span class="line">返回值：string</span><br><span class="line">说明：去除字符串右边的空格</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">rtrim</span>(<span class="string">&#x27; abc &#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：abc</span><br></pre></td></tr></table></figure></p><h5 id="正则表达式替换函数-regexp-replace"><a href="#正则表达式替换函数-regexp-replace" class="headerlink" title="正则表达式替换函数 regexp_replace"></a>正则表达式替换函数 regexp_replace</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：regexp_replace(string A, string B, string C)</span><br><span class="line">返回值：string</span><br><span class="line">说明：将字符串 A 中的符合 Java 正则表达式 B 的部分替换为 C。注意，在有些情况下要使用转义字符，类似 oracle 中的 regexp_replace 函数。</span><br><span class="line">示例：<span class="keyword">select</span> regexp_replace(<span class="string">&#x27;foobar&#x27;</span>, <span class="string">&#x27;oo|ar&#x27;</span>, <span class="string">&#x27;&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：fb</span><br></pre></td></tr></table></figure></p><h5 id="正则表达式解析函数-regexp-extract"><a href="#正则表达式解析函数-regexp-extract" class="headerlink" title="正则表达式解析函数 regexp_extract"></a>正则表达式解析函数 regexp_extract</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">语法：regexp_extract(string subject, string pattern, int index)</span><br><span class="line">返回值：string</span><br><span class="line">说明：将字符串 subject 按照 pattern 正则表达式的规则拆分，返回 index 指定的字符。</span><br><span class="line">示例：<span class="keyword">select</span> regexp_extract(<span class="string">&#x27;foothebar&#x27;</span>, <span class="string">&#x27;foo(.*?)(bar)&#x27;</span>, <span class="number">1</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：the</span><br><span class="line">示例：<span class="keyword">select</span> regexp_extract(<span class="string">&#x27;foothebar&#x27;</span>, <span class="string">&#x27;foo(.*?)(bar)&#x27;</span>, <span class="number">2</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：bar</span><br><span class="line">示例：<span class="keyword">select</span> regexp_extract(<span class="string">&#x27;foothebar&#x27;</span>, <span class="string">&#x27;foo(.*?)(bar)&#x27;</span>, <span class="number">0</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：foothebar</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>在有些情况下要使用转义字符，下面的等号要用双竖线转义，这是 Java 正则表达式的规则。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> data_field,</span><br><span class="line">regexp_extract(data_field, <span class="string">&#x27;.*?bgStart\\=([^&amp;]+)&#x27;</span>,<span class="number">1</span>) <span class="keyword">as</span> aaa,</span><br><span class="line">regexp_extract(data_field, <span class="string">&#x27;.*?contentLoaded_headStart\\=([^&amp;]+)&#x27;</span>,<span class="number">1</span>) <span class="keyword">as</span> bbb,</span><br><span class="line">regexp_extract(data_field, <span class="string">&#x27;.*?AppLoad2Req\\=([^&amp;]+)&#x27;</span>,<span class="number">1</span>) <span class="keyword">as</span> ccc </span><br><span class="line"><span class="keyword">from</span> pt_nginx_loginlog_st </span><br><span class="line"><span class="keyword">where</span> pt = <span class="string">&#x27;2021-03-28&#x27;</span> <span class="keyword">limit</span> <span class="number">2</span>;</span><br></pre></td></tr></table></figure></p><h5 id="URL-解析函数-parse-url"><a href="#URL-解析函数-parse-url" class="headerlink" title="URL 解析函数 parse_url"></a>URL 解析函数 parse_url</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：parse_url(string urlString, string partToExtract [, string keyToExtract])</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回 URL 中指定的部分。partToExtract 的有效值为：HOST，PATH，QUERY，REF，PROTOCOL，AUTHORITY，FILE，USERINFO。</span><br><span class="line">示例：<span class="keyword">select</span> parse_url(<span class="string">&#x27;https://www.tableName.com/path1/p.php?k1=v1&amp;k2=v2#Ref1&#x27;</span>, <span class="string">&#x27;HOST&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：www.tableName.com </span><br><span class="line">示例：<span class="keyword">select</span> parse_url(<span class="string">&#x27;https://www.tableName.com/path1/p.php?k1=v1&amp;k2=v2#Ref1&#x27;</span>, <span class="string">&#x27;QUERY&#x27;</span>, <span class="string">&#x27;k1&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：v1</span><br></pre></td></tr></table></figure></p><h5 id="JSON-解析函数-get-json-object"><a href="#JSON-解析函数-get-json-object" class="headerlink" title="JSON 解析函数 get_json_object"></a>JSON 解析函数 get_json_object</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：get_json_object(string json_string, string path)</span><br><span class="line">返回值：string</span><br><span class="line">说明：解析 JSON 的字符串 json_string，返回 path 指定的内容。如果输入的 JSON 字符串无效，那么返回 NULL。</span><br><span class="line">示例：<span class="keyword">select</span> get_json_object(<span class="string">&#x27;&#123;&quot;store&quot;:&#123;&quot;fruit&quot;:\[&#123;&quot;weight&quot;:8,&quot;type&quot;:&quot;apple&quot;&#125;,&#123;&quot;weight&quot;:9,&quot;type&quot;:&quot;pear&quot;&#125;], &quot;bicycle&quot;:&#123;&quot;price&quot;:19.95,&quot;color&quot;:&quot;red&quot;&#125; &#125;,&quot;email&quot;:&quot;amy@only_for_json_udf_test.net&quot;,&quot;owner&quot;:&quot;amy&quot;&#125;&#x27;</span>, <span class="string">&#x27;$.owner&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：amy</span><br></pre></td></tr></table></figure></p><h5 id="空格字符串函数-space"><a href="#空格字符串函数-space" class="headerlink" title="空格字符串函数 space"></a>空格字符串函数 space</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">语法：space(int n)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回长度为 n 的字符串</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">space</span>(<span class="number">10</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">length</span>(<span class="keyword">space</span>(<span class="number">10</span>)) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：10</span><br></pre></td></tr></table></figure></p><h5 id="重复字符串函数-repeat"><a href="#重复字符串函数-repeat" class="headerlink" title="重复字符串函数 repeat"></a>重复字符串函数 repeat</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：repeat(string str, int n)</span><br><span class="line">返回值：string</span><br><span class="line">说明：返回重复 n 次后的 str 字符串</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">repeat</span>(<span class="string">&#x27;abc&#x27;</span>, <span class="number">5</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：abcabcabcabcabc</span><br></pre></td></tr></table></figure></p><h5 id="首字符-ascii-函数"><a href="#首字符-ascii-函数" class="headerlink" title="首字符 ascii 函数"></a>首字符 ascii 函数</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：ascii(string str)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回字符串 str 第一个字符的 ascii 码</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">ascii</span>(<span class="string">&#x27;abcde&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：97</span><br></pre></td></tr></table></figure></p><h5 id="左补足函数-lpad"><a href="#左补足函数-lpad" class="headerlink" title="左补足函数 lpad"></a>左补足函数 lpad</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">语法：lpad(string str, int len, string pad)</span><br><span class="line">返回值：string</span><br><span class="line">说明：将 str 进行用 pad 进行左补足到 len 位</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">lpad</span>(<span class="string">&#x27;abc&#x27;</span>, <span class="number">10</span>, <span class="string">&#x27;td&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：tdtdtdtabc</span><br><span class="line">注意：与 GP，ORACLE 不同，pad 不能默认</span><br></pre></td></tr></table></figure></p><h5 id="右补足函数-rpad"><a href="#右补足函数-rpad" class="headerlink" title="右补足函数 rpad"></a>右补足函数 rpad</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：rpad(string str, int len, string pad)</span><br><span class="line">返回值：string</span><br><span class="line">说明：将 str 进行用 pad 进行右补足到 len 位</span><br><span class="line">示例：<span class="keyword">select</span> rpad(<span class="string">&#x27;abc&#x27;</span>, <span class="number">10</span>, <span class="string">&#x27;td&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：abctdtdtdt</span><br></pre></td></tr></table></figure></p><h5 id="分割字符串函数-split"><a href="#分割字符串函数-split" class="headerlink" title="分割字符串函数 split"></a>分割字符串函数 split</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：split(string str, string pat)</span><br><span class="line">返回值：array</span><br><span class="line">说明：按照 pat 字符串分割 str，会返回分割后的字符串数组</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">split</span>(<span class="string">&#x27;abtcdtef&#x27;</span>, <span class="string">&#x27;t&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：[&quot;ab&quot;,&quot;cd&quot;,&quot;ef&quot;]</span><br></pre></td></tr></table></figure></p><h5 id="集合查找函数-find-in-set"><a href="#集合查找函数-find-in-set" class="headerlink" title="集合查找函数 find_in_set"></a>集合查找函数 find_in_set</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：find_in_set(string str, string strList)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回 str 在 strlist 第一次出现的位置，strlist 是用逗号分割的字符串。如果没有找该 str 字符，则返回 0</span><br><span class="line">示例：<span class="keyword">select</span> find_in_set(<span class="string">&#x27;ab&#x27;</span>, <span class="string">&#x27;ef,ab,de&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：2</span><br><span class="line">示例：<span class="keyword">select</span> find_in_set(<span class="string">&#x27;at&#x27;</span>, <span class="string">&#x27;ef,ab,de&#x27;</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：0</span><br></pre></td></tr></table></figure></p><h4 id="复合类型构建操作"><a href="#复合类型构建操作" class="headerlink" title="复合类型构建操作"></a>复合类型构建操作</h4><h5 id="Map-类型构建"><a href="#Map-类型构建" class="headerlink" title="Map 类型构建"></a>Map 类型构建</h5><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：map (key1, value1, key2, value2, …)</span><br><span class="line">说明：根据输入的 key 和 value 对构建 Map 类型</span><br><span class="line">示例：<span class="keyword">Create</span> <span class="keyword">table</span> mapTable <span class="keyword">as</span> <span class="keyword">select</span> <span class="keyword">map</span>(<span class="string">&#x27;100&#x27;</span>, <span class="string">&#x27;tom&#x27;</span>, <span class="string">&#x27;200&#x27;</span>, <span class="string">&#x27;mary&#x27;</span>) <span class="keyword">as</span> t <span class="keyword">from</span> tableName;</span><br><span class="line">示例：<span class="keyword">describe</span> mapTable;</span><br><span class="line">结果：t    map&lt;string ,string&gt;</span><br><span class="line">示例：<span class="keyword">select</span> t <span class="keyword">from</span> tableName;</span><br><span class="line">结果：&#123;&quot;100&quot;:&quot;tom&quot;,&quot;200&quot;:&quot;mary&quot;&#125;</span><br></pre></td></tr></table></figure></p><h5 id="Struct-类型构建"><a href="#Struct-类型构建" class="headerlink" title="Struct 类型构建"></a>Struct 类型构建</h5><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：struct(val1, val2, val3, …)</span><br><span class="line">说明：根据输入的参数构建结构体 Struct 类型</span><br><span class="line">示例：<span class="keyword">create</span> <span class="keyword">table</span> struct_table <span class="keyword">as</span> <span class="keyword">select</span> <span class="keyword">struct</span>(<span class="string">&#x27;tom&#x27;</span>, <span class="string">&#x27;mary&#x27;</span>, <span class="string">&#x27;tim&#x27;</span>) <span class="keyword">as</span> t <span class="keyword">from</span> tableName;</span><br><span class="line">示例：<span class="keyword">describe</span> struct_table;</span><br><span class="line">结果：t    struct&lt;col1:string,col2:string,col3:string&gt;</span><br><span class="line">示例：<span class="keyword">select</span> t <span class="keyword">from</span> tableName;</span><br><span class="line">结果：&#123;&quot;col1&quot;:&quot;tom&quot;,&quot;col2&quot;:&quot;mary&quot;,&quot;col3&quot;:&quot;tim&quot;&#125;</span><br></pre></td></tr></table></figure></p><h5 id="Array-类型构建"><a href="#Array-类型构建" class="headerlink" title="Array 类型构建"></a>Array 类型构建</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">语法：array(val1, val2, …)</span><br><span class="line">说明：根据输入的参数构建数组 Array 类型</span><br><span class="line">示例：<span class="keyword">create</span> <span class="keyword">table</span> arr_table <span class="keyword">as</span> <span class="keyword">select</span> <span class="built_in">array</span>(<span class="string">&#x27;tom&#x27;</span>, <span class="string">&#x27;mary&#x27;</span>, <span class="string">&#x27;tim&#x27;</span>) <span class="keyword">as</span> t <span class="keyword">from</span> tableName;</span><br><span class="line">示例：<span class="keyword">describe</span> tableName;</span><br><span class="line">结果：t    array&lt;string&gt;</span><br><span class="line">示例：<span class="keyword">select</span> t <span class="keyword">from</span> tableName;</span><br><span class="line">结果：[&quot;tom&quot;,&quot;mary&quot;,&quot;tim&quot;]</span><br></pre></td></tr></table></figure></p><h4 id="复杂类型访问操作"><a href="#复杂类型访问操作" class="headerlink" title="复杂类型访问操作"></a>复杂类型访问操作</h4><h5 id="Array-类型访问-A-n"><a href="#Array-类型访问-A-n" class="headerlink" title="Array 类型访问: A[n]"></a>Array 类型访问: A[n]</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">语法：A[n]</span><br><span class="line">操作类型: A 为 array 类型，n 为 int 类型</span><br><span class="line">说明：返回数组 A 中的第 n 个变量值。数组的起始下标为 0。比如，A 是个值为 [&#x27;foo&#x27;, &#x27;bar&#x27;] 的数组类型，那么 A[0] 将返回 &#x27;foo&#x27;，而 A[1] 将返回 &#x27;bar&#x27;</span><br><span class="line">示例：<span class="keyword">create</span> <span class="keyword">table</span> arr_table2 <span class="keyword">as</span> <span class="keyword">select</span> <span class="built_in">array</span>(<span class="string">&quot;tom&quot;</span>,<span class="string">&quot;mary&quot;</span>,<span class="string">&quot;tim&quot;</span>) <span class="keyword">as</span> t <span class="keyword">from</span> tableName;</span><br><span class="line">示例：<span class="keyword">select</span> t[<span class="number">0</span>], t[<span class="number">1</span>] <span class="keyword">from</span> arr_table2;</span><br><span class="line">结果：tom    mary    tim</span><br></pre></td></tr></table></figure></p><h5 id="Map-类型访问-M-key"><a href="#Map-类型访问-M-key" class="headerlink" title="Map 类型访问: M[key]"></a>Map 类型访问: M[key]</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">语法：M[key]</span><br><span class="line">操作类型: M 为 map 类型，key 为 map 中的 key 值</span><br><span class="line">说明：返回 map 类型 M 中，key 值为指定值的 value 值。比如，M 是值为 &#123;&#x27;f&#x27; -&gt; &#x27;foo&#x27;, &#x27;b&#x27; -&gt; &#x27;bar&#x27;, &#x27;all&#x27; -&gt; &#x27;foobar&#x27;&#125; 的 map 类型，那么 M[&#x27;all&#x27;] 将会返回 &#x27;foobar&#x27;</span><br><span class="line">示例：<span class="keyword">Create</span> <span class="keyword">table</span> map_table2 <span class="keyword">as</span> <span class="keyword">select</span> <span class="keyword">map</span>(<span class="string">&#x27;100&#x27;</span>, <span class="string">&#x27;tom&#x27;</span>, <span class="string">&#x27;200&#x27;</span>, <span class="string">&#x27;mary&#x27;</span>) <span class="keyword">as</span> t <span class="keyword">from</span> tableName;</span><br><span class="line">示例：<span class="keyword">select</span> t[<span class="string">&#x27;200&#x27;</span>], t[<span class="string">&#x27;100&#x27;</span>] <span class="keyword">from</span> map_table2;</span><br><span class="line">结果：mary    tom</span><br></pre></td></tr></table></figure></p><h5 id="Struct-类型访问-S-x"><a href="#Struct-类型访问-S-x" class="headerlink" title="Struct 类型访问 S.x"></a>Struct 类型访问 S.x</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">语法：S.x</span><br><span class="line">操作类型: S 为 struct 类型</span><br><span class="line">说明：返回结构体 S 中的 x 字段。比如，对于结构体 struct foobar &#123;int foo, int bar&#125;，foobar.foo 返回结构体中的 foo 字段</span><br><span class="line">示例：<span class="keyword">create</span> <span class="keyword">table</span> str_table2 <span class="keyword">as</span> <span class="keyword">select</span> <span class="keyword">struct</span>(<span class="string">&#x27;tom&#x27;</span>, <span class="string">&#x27;mary&#x27;</span>, <span class="string">&#x27;tim&#x27;</span>) <span class="keyword">as</span> t <span class="keyword">from</span> tableName;</span><br><span class="line">示例：<span class="keyword">describe</span> tableName;</span><br><span class="line">结果：t    struct&lt;col1:string,col2:string,col3:string&gt;</span><br><span class="line">示例：<span class="keyword">select</span> t.col1, t.col3 <span class="keyword">from</span> str_table2;</span><br><span class="line">结果：tom    tim</span><br></pre></td></tr></table></figure></p><h4 id="复杂类型长度统计函数"><a href="#复杂类型长度统计函数" class="headerlink" title="复杂类型长度统计函数"></a>复杂类型长度统计函数</h4><h5 id="Map-类型长度函数-size-Map"><a href="#Map-类型长度函数-size-Map" class="headerlink" title="Map 类型长度函数 size(Map)"></a>Map 类型长度函数 size(Map<k .v>)</k></h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：size(Map&lt;k .V&gt;)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回 map 类型的长度</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">size</span>(t) <span class="keyword">from</span> map_table2;</span><br><span class="line">结果：2</span><br></pre></td></tr></table></figure></p><h5 id="Array-类型长度函数-size-Array"><a href="#Array-类型长度函数-size-Array" class="headerlink" title="Array 类型长度函数 size(Array)"></a>Array 类型长度函数 size(Array)</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：size(Array&lt;T&gt;)</span><br><span class="line">返回值：int</span><br><span class="line">说明：返回 array 类型的长度</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">size</span>(t) <span class="keyword">from</span> arr_table2;</span><br><span class="line">结果：4</span><br></pre></td></tr></table></figure></p><h5 id="类型转换函数：cast"><a href="#类型转换函数：cast" class="headerlink" title="类型转换函数：cast"></a>类型转换函数：cast</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">语法：cast(expr as &lt;type&gt;)</span><br><span class="line">返回值：Expected &quot;=&quot; to follow &quot;type&quot;</span><br><span class="line">说明：返回转换后的数据类型</span><br><span class="line">示例：<span class="keyword">select</span> <span class="keyword">cast</span>(<span class="string">&#x27;1&#x27;</span> <span class="keyword">as</span> <span class="built_in">bigint</span>) <span class="keyword">from</span> tableName;</span><br><span class="line">结果：1</span><br></pre></td></tr></table></figure></p><h3 id="行转列及列转行"><a href="#行转列及列转行" class="headerlink" title="行转列及列转行"></a>行转列及列转行</h3><h4 id="使用-explode-函数将-Hive-表中的-Map-和-Array-字段数据进行拆分"><a href="#使用-explode-函数将-Hive-表中的-Map-和-Array-字段数据进行拆分" class="headerlink" title="使用 explode 函数将 Hive 表中的 Map 和 Array 字段数据进行拆分"></a>使用 <code>explode</code> 函数将 Hive 表中的 Map 和 Array 字段数据进行拆分</h4><p><code>lateral view</code> 用于和 <code>split</code>、<code>explode</code> 等 UDTF 一起使用的，能将一行数据拆分成多行数据，在此基础上可以对拆分的数据进行聚合，<code>lateral view</code> 首先为原始表的每行调用 UDTF，UDTF 会把一行拆分成一行或者多行，<code>lateral view</code> 在把结果组合，产生一个支持别名表的虚拟表。</p><p>其中 <code>explode</code> 还可以用于将 Hive 一列中复杂的 array 或者 map 结构拆分成多行。</p><p><strong>需求：</strong></p><p>现在有数据格式如下</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">zhangsan    child1,child2,child3,child4    k1:v1,k2:v2</span><br><span class="line">lisi    child5,child6,child7,child8    k3:v3,k4:v4</span><br></pre></td></tr></table></figure></p><p>字段之间使用 <code>\t</code> 分割，需求将所有的 child 进行拆开成为一列</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">+<span class="comment">----------+--+</span></span><br><span class="line">| mychild |</span><br><span class="line">+<span class="comment">----------+--+</span></span><br><span class="line">| child1  |</span><br><span class="line">| child2  |</span><br><span class="line">| child3  |</span><br><span class="line">| child4  |</span><br><span class="line">| child5  |</span><br><span class="line">| child6  |</span><br><span class="line">| child7  |</span><br><span class="line">| child8  |</span><br><span class="line">+<span class="comment">----------+--+</span></span><br></pre></td></tr></table></figure></p><p>将 map 的 key 和 value 也进行拆开，成为如下结果</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">+<span class="comment">-----------+-------------+--+</span></span><br><span class="line">| mymapkey | mymapvalue |</span><br><span class="line">+<span class="comment">-----------+-------------+--+</span></span><br><span class="line">| k1    | v1     |</span><br><span class="line">| k2    | v2     |</span><br><span class="line">| k3    | v3     |</span><br><span class="line">| k4    | v4     |</span><br><span class="line">+<span class="comment">-----------+-------------+--+</span></span><br></pre></td></tr></table></figure></p><ol><li><p>创建 hive 数据库</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hive (default)&gt; create database hive_explode;</span><br><span class="line">hive (default)&gt; use hive_explode;</span><br></pre></td></tr></table></figure></p></li><li><p>创建 Hive 表，然后使用 <code>explode</code> 拆分 map 和 array</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; create table t3(name string,children array&lt;string&gt;,address Map&lt;string,string&gt;) row format delimited fields terminated by &#x27;\t&#x27; collection items terminated by &#x27;,&#x27; map keys terminated by &#x27;:&#x27; stored as textFile;</span><br></pre></td></tr></table></figure></p></li><li><p>加载数据 node03 执行以下命令创建表数据文件</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p /export/servers/hivedatas/</span><br><span class="line">cd /export/servers/hivedatas/</span><br><span class="line">vim maparray</span><br></pre></td></tr></table></figure></p><p> 内容如下:</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">zhangsan child1,child2,child3,child4 k1:v1,k2:v2</span><br><span class="line">lisi child5,child6,child7,child8 k3:v3,k4:v4</span><br></pre></td></tr></table></figure></p><p> Hive 表当中加载数据</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; load data local inpath &#x27;/export/servers/hivedatas/maparray&#x27; into table t3;</span><br></pre></td></tr></table></figure></p></li><li><p>使用 <code>explode</code> 将 Hive 当中数据拆开</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将 array 当中的数据拆分开</span></span><br><span class="line">hive (hive_explode)&gt; SELECT explode(children) AS myChild FROM t3;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将 map 当中的数据拆分开</span></span><br><span class="line">hive (hive_explode)&gt; SELECT explode(address) AS (myMapKey, myMapValue) FROM t3;</span><br></pre></td></tr></table></figure></p></li></ol><h4 id="使用-explode-拆分-json-字符串"><a href="#使用-explode-拆分-json-字符串" class="headerlink" title="使用 explode 拆分 json 字符串"></a>使用 explode 拆分 json 字符串</h4><p><strong>需求：</strong></p><p>现在有一些数据格式如下：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">a:shandong,b:beijing,c:hebei|1,2,3,4,5,6,7,8,9|[&#123;&quot;source&quot;:&quot;7fresh&quot;,&quot;monthSales&quot;:4900,&quot;userCount&quot;:1900,&quot;score&quot;:&quot;9.9&quot;&#125;,&#123;&quot;source&quot;:&quot;jd&quot;,&quot;monthSales&quot;:2090,&quot;userCount&quot;:78981,&quot;score&quot;:&quot;9.8&quot;&#125;,&#123;&quot;source&quot;:&quot;jdmart&quot;,&quot;monthSales&quot;:6987,&quot;userCount&quot;:1600,&quot;score&quot;:&quot;9.0&quot;&#125;]</span><br></pre></td></tr></table></figure></p><p>其中字段与字段之间的分隔符是 <code>|</code>。</p><p>我们要解析得到所有的 monthSales 对应的值为以下这一列（行转列）</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">4900</span><br><span class="line">2090</span><br><span class="line">6987</span><br></pre></td></tr></table></figure></p><ol><li><p>创建 Hive 表</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> explode_lateral_view</span><br><span class="line">(<span class="string">`area`</span> <span class="keyword">string</span>,</span><br><span class="line"><span class="string">`goods_id`</span> <span class="keyword">string</span>,</span><br><span class="line"><span class="string">`sale_info`</span> <span class="keyword">string</span>)</span><br><span class="line"><span class="keyword">ROW</span> <span class="keyword">FORMAT</span> <span class="keyword">DELIMITED</span></span><br><span class="line"><span class="keyword">FIELDS</span> <span class="keyword">TERMINATED</span> <span class="keyword">BY</span> <span class="string">&#x27;|&#x27;</span></span><br><span class="line"><span class="keyword">STORED</span> <span class="keyword">AS</span> textfile;</span><br></pre></td></tr></table></figure></p></li><li><p>准备数据并加载数据</p><p> 准备数据如下</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">cd /export/servers/hivedatas</span><br><span class="line">vim explode_json</span><br><span class="line"></span><br><span class="line">a:shandong,b:beijing,c:hebei|1,2,3,4,5,6,7,8,9|[&#123;&quot;source&quot;:&quot;7fresh&quot;,&quot;monthSales&quot;:4900,&quot;userCount&quot;:1900,&quot;score&quot;:&quot;9.9&quot;&#125;,&#123;&quot;source&quot;:&quot;jd&quot;,&quot;monthSales&quot;:2090,&quot;userCount&quot;:78981,&quot;score&quot;:&quot;9.8&quot;&#125;,&#123;&quot;source&quot;:&quot;jdmart&quot;,&quot;monthSales&quot;:6987,&quot;userCount&quot;:1600,&quot;score&quot;:&quot;9.0&quot;&#125;]</span><br></pre></td></tr></table></figure></p><p> 加载数据到 Hive 表当中去</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; load data local inpath &#x27;/export/servers/hivedatas/explode_json&#x27; overwrite into table explode_lateral_view;</span><br></pre></td></tr></table></figure></p></li><li><p>使用 <code>explode</code> 拆分 Array</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; select explode(split(goods_id, &#x27;,&#x27;)) as goods_id from explode_lateral_view;</span><br></pre></td></tr></table></figure></p></li><li><p>使用 <code>explode</code> 拆解 Map</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; select explode(split(area, &#x27;,&#x27;)) as area from explode_lateral_view;</span><br></pre></td></tr></table></figure></p></li><li><p>拆解 JSON 字段</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; select explode(split(regexp_replace(regexp_replace(sale_info, &#x27;\\[\\&#123;&#x27;, &#x27;&#x27;), &#x27;&#125;]&#x27;, &#x27;&#x27;), &#x27;&#125;,\\&#123;&#x27;)) as sale_info from explode_lateral_view;</span><br></pre></td></tr></table></figure></p><p> 然后我们想用 <code>get_json_object</code> 来获取 key 为 monthSales 的数据：</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; select get_json_object(explode(split(regexp_replace(regexp_replace(sale_info, &#x27;\\[\\&#123;&#x27;, &#x27;&#x27;), &#x27;&#125;]&#x27;, &#x27;&#x27;), &#x27;&#125;,\\&#123;&#x27;)), &#x27;$.monthSales&#x27;) as sale_info from explode_lateral_view;</span><br></pre></td></tr></table></figure></p><p> 然后挂了 <code>FAILED: SemanticException [Error 10081]: UDTF&#39;s are not supported outside the SELECT clause, nor nested in expressions</code></p><p> UDTF <code>explode</code> 不能写在别的函数内</p><p> 如果你这么写，想查两个字段，<code>select explode(split(area, &#39;,&#39;)) as area, good_id from explode_lateral_view;</code></p><p> 会报错 <code>FAILED: SemanticException 1:40 Only a single expression in the SELECT clause is supported with UDTF&#39;s. Error encountered near token &#39;good_id&#39;</code></p><p> 使用 UDTF 的时候，只支持一个字段，这时候就需要 LATERAL VIEW 出场了</p></li></ol><h4 id="配合-LATERAL-VIEW-使用"><a href="#配合-LATERAL-VIEW-使用" class="headerlink" title="配合 LATERAL VIEW 使用"></a>配合 LATERAL VIEW 使用</h4><p>配合 <code>lateral view</code> 查询多个字段</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; select goods_id2, sale_info from explode_lateral_view LATERAL VIEW explode(split(goods_id, &#x27;,&#x27;)) goods as goods_id2;</span><br></pre></td></tr></table></figure></p><p>其中 <code>LATERAL VIEW explode(split(goods_id,&#39;,&#39;)) goods</code>相当于一个虚拟表，与原表 <code>explode_lateral_view</code> 笛卡尔积关联</p><p>也可以多重使用</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; select goods_id2, sale_info, area2</span><br><span class="line">          from explode_lateral_view </span><br><span class="line">          LATERAL VIEW explode(split(goods_id, &#x27;,&#x27;)) goods as goods_id2 </span><br><span class="line">          LATERAL VIEW explode(split(area, &#x27;,&#x27;)) area as area2;</span><br></pre></td></tr></table></figure></p><p>也是三个表笛卡尔积的结果。</p><p>最终，我们可以通过下面的句子，把这个 JSON 格式的一行数据，完全转换成二维表的方式展现</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; select get_json_object(concat(&#x27;&#123;&#x27;, sale_info_1 , &#x27;&#125;&#x27;), &#x27;$.source&#x27;) as source,</span><br><span class="line">              get_json_object(concat(&#x27;&#123;&#x27;, sale_info_1, &#x27;&#125;&#x27;), &#x27;$.monthSales&#x27;) as monthSales,</span><br><span class="line">              get_json_object(concat(&#x27;&#123;&#x27;, sale_info_1, &#x27;&#125;&#x27;), &#x27;$.userCount&#x27;) as monthSales,</span><br><span class="line">              get_json_object(concat(&#x27;&#123;&#x27;, sale_info_1, &#x27;&#125;&#x27;), &#x27;$.score&#x27;) as monthSales</span><br><span class="line">          from explode_lateral_view</span><br><span class="line">          LATERAL VIEW explode(split(regexp_replace(regexp_replace(sale_info, &#x27;\\[\\&#123;&#x27;, &#x27;&#x27;), &#x27;&#125;]&#x27;, &#x27;&#x27;), &#x27;&#125;,\\&#123;&#x27;)) sale_info as sale_info_1;</span><br></pre></td></tr></table></figure></p><p><strong>总结：</strong></p><p><code>Lateral View</code> 通常和 UDTF 一起出现，为了解决 UDTF 不允许在 <code>select</code> 字段的问题。<code>Multiple Lateral View</code> 可以实现类似笛卡尔乘积。<code>Outer</code> 关键字可以把不输出的 UDTF 的空结果，输出成 NULL，防止丢失数据。</p><h4 id="行转列"><a href="#行转列" class="headerlink" title="行转列"></a>行转列</h4><p><strong>相关参数说明:</strong></p><ul><li><p><code>CONCAT(string A/col, string B/col…)</code>：返回输入字符串连接后的结果，支持任意个输入字符串;</p></li><li><p><code>CONCAT_WS(separator, str1, str2,...)</code>：它是一个特殊形式的 <code>CONCAT()</code>。第一个参数是剩余参数间的分隔符。分隔符可以是与剩余参数一样的字符串。如果分隔符是 NULL，返回值也将为 NULL。这个函数会跳过分隔符参数后的任何 NULL 和空字符串。分隔符将被加到被连接的字符串之间;</p></li><li><p><code>COLLECT_SET(col)</code>：函数只接受基本数据类型，它的主要作用是将某字段的值进行去重汇总，产生 Array 类型字段。</p></li></ul><p><strong>数据准备:</strong></p><div class="table-container"><table><thead><tr><th>name</th><th>constellation</th><th>blood_type</th></tr></thead><tbody><tr><td>孙悟空</td><td>白羊座</td><td>A</td></tr><tr><td>老王</td><td>射手座</td><td>A</td></tr><tr><td>宋宋</td><td>白羊座</td><td>B</td></tr><tr><td>猪八戒</td><td>白羊座</td><td>A</td></tr><tr><td>凤姐</td><td>射手座</td><td>A</td></tr></tbody></table></div><p><strong>需求: </strong></p><p>把星座和血型一样的人归类到一起。结果如下：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">射手座,A      老王|凤姐</span><br><span class="line">白羊座,A      孙悟空|猪八戒</span><br><span class="line">白羊座,B      宋宋</span><br></pre></td></tr></table></figure></p><p><strong>实现步骤:</strong></p><ol><li><p>创建本地 <code>constellation.txt</code>，导入数据 node03 服务器执行以下命令创建文件，注意数据使用 <code>\t</code> 进行分割</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">cd /export/servers/hivedatas</span><br><span class="line">vim constellation.txt</span><br><span class="line"></span><br><span class="line">数据如下：</span><br><span class="line">孙悟空 白羊座 A</span><br><span class="line">老王 射手座 A</span><br><span class="line">宋宋 白羊座 B</span><br><span class="line">猪八戒 白羊座 A</span><br><span class="line">凤姐 射手座 A</span><br></pre></td></tr></table></figure></p></li><li><p>创建 Hive 表并导入数据</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建 Hive 表并加载数据</span></span><br><span class="line">hive (hive_explode)&gt; create table person_info(</span><br><span class="line">          name string, </span><br><span class="line">          constellation string, </span><br><span class="line">          blood_type string) </span><br><span class="line">          row format delimited fields terminated by &quot;\t&quot;;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载数据</span></span><br><span class="line">hive (hive_explode)&gt; load data local inpath &#x27;/export/servers/hivedatas/constellation.txt&#x27; into table person_info;</span><br></pre></td></tr></table></figure></p></li><li><p>按需求查询数据</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; select t1.base, concat_ws(&#x27;|&#x27;, collect_set(t1.name)) name</span><br><span class="line">          from (<span class="keyword">select</span> <span class="keyword">name</span>, <span class="keyword">concat</span>(constellation, <span class="string">&quot;,&quot;</span> , blood_type) base</span><br><span class="line">                  <span class="keyword">from</span> person_info) t1</span><br><span class="line">          <span class="keyword">group</span> <span class="keyword">by</span> t1.base;</span><br></pre></td></tr></table></figure></p></li></ol><h4 id="列转行"><a href="#列转行" class="headerlink" title="列转行"></a>列转行</h4><p><strong>所需函数:</strong></p><p><code>EXPLODE(col)</code>：将 Hive 一列中复杂的 array 或者 map 结构拆分成多行。</p><p><code>LATERAL VIEW</code>：用于和 split, explode 等 UDTF 一起使用，它能够将一列数据拆成多行数据，在此基础上可以对拆分后的数据进行聚合。</p><p><strong>数据准备:</strong></p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /export/servers/hivedatas</span><br><span class="line">vim movie.txt</span><br></pre></td></tr></table></figure></p><p>文件内容如下: 数据字段之间使用 <code>\t</code> 进行分割</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">《疑犯追踪》 悬疑,动作,科幻,剧情</span><br><span class="line">《Lie to me》 悬疑,警匪,动作,心理,剧情</span><br><span class="line">《战狼2》 战争,动作,灾难</span><br></pre></td></tr></table></figure></p><p><strong>需求: </strong></p><p>将电影分类中的数组数据展开。结果如下：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">《疑犯追踪》 悬疑</span><br><span class="line">《疑犯追踪》 动作</span><br><span class="line">《疑犯追踪》 科幻</span><br><span class="line">《疑犯追踪》 剧情</span><br><span class="line">《Lie to me》 悬疑</span><br><span class="line">《Lie to me》 警匪</span><br><span class="line">《Lie to me》 动作</span><br><span class="line">《Lie to me》 心理</span><br><span class="line">《Lie to me》 剧情</span><br><span class="line">《战狼2》 战争</span><br><span class="line">《战狼2》 动作</span><br><span class="line">《战狼2》 灾难</span><br></pre></td></tr></table></figure></p><p><strong>实现步骤:</strong></p><ol><li><p>创建 Hive 表</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> movie_info(</span><br><span class="line">  movie <span class="keyword">string</span>, </span><br><span class="line">  <span class="keyword">category</span> <span class="built_in">array</span>&lt;<span class="keyword">string</span>&gt;) </span><br><span class="line"><span class="keyword">row</span> <span class="keyword">format</span> <span class="keyword">delimited</span> <span class="keyword">fields</span> <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&quot;\t&quot;</span> </span><br><span class="line">collection items <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&quot;,&quot;</span>;</span><br></pre></td></tr></table></figure></p></li><li><p>加载数据</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&quot;/export/servers/hivedatas/movie.txt&quot;</span> <span class="keyword">into</span> <span class="keyword">table</span> movie_info;</span><br></pre></td></tr></table></figure></p></li><li><p>按需求查询数据</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> movie, category_name</span><br><span class="line">  <span class="keyword">from</span> movie_info <span class="keyword">lateral</span> <span class="keyword">view</span> <span class="keyword">explode</span>(<span class="keyword">category</span>) table_tmp <span class="keyword">as</span> category_name;</span><br></pre></td></tr></table></figure></p></li></ol><h4 id="REFLECT-函数"><a href="#REFLECT-函数" class="headerlink" title="REFLECT 函数"></a>REFLECT 函数</h4><p><code>reflect</code> 函数可以支持在 SQL 中调用 <code>Java</code> 中的自带函数，秒杀一切 UDF 函数。</p><p><strong>需求 1: 使用 java.lang.Math 当中的 Max 求两列中最大值</strong></p><p><strong>实现步骤:</strong></p><ol><li><p>创建 Hive 表</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> test_udf(col1 <span class="built_in">int</span>, col2 <span class="built_in">int</span>) <span class="keyword">row</span> <span class="keyword">format</span> <span class="keyword">delimited</span> <span class="keyword">fields</span> <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&#x27;,&#x27;</span>;</span><br></pre></td></tr></table></figure></p></li><li><p>准备数据并加载数据</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /export/servers/hivedatas</span><br><span class="line">vim test_udf </span><br></pre></td></tr></table></figure></p><p> 文件内容如下:</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">1,2</span><br><span class="line">4,3</span><br><span class="line">6,4</span><br><span class="line">7,5</span><br><span class="line">5,6</span><br></pre></td></tr></table></figure></p></li><li><p>加载数据</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; load data local inpath &#x27;/export/servers/hivedatas/test_udf&#x27; overwrite into table test_udf;</span><br></pre></td></tr></table></figure></p></li><li><p>使用 <code>java.lang.Math</code> 当中的 <code>Max</code> 求两列当中的最大值</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; select reflect(&quot;java.lang.Math&quot;, &quot;max&quot;, col1, col2) from test_udf;</span><br></pre></td></tr></table></figure></p></li></ol><p><strong>需求 2: 文件中不同的记录来执行不同的 Java 的内置函数</strong></p><p><strong>实现步骤:</strong></p><ol><li><p>创建 Hive 表</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; create table test_udf2(class_name string, method_name string, col1 int , col2 int) row format delimited fields terminated by &#x27;,&#x27;;</span><br></pre></td></tr></table></figure></p></li><li><p>准备数据</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /export/servers/hivedatas</span><br><span class="line">vim test_udf2</span><br></pre></td></tr></table></figure></p><p> 文件内容如下:</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">java.lang.Math,min,1,2</span><br><span class="line">java.lang.Math,max,2,3</span><br></pre></td></tr></table></figure></p></li><li><p>加载数据</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; load data local inpath &#x27;/export/servers/hivedatas/test_udf2&#x27; overwrite into table test_udf2;</span><br></pre></td></tr></table></figure></p></li><li><p>执行查询</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hive (hive_explode)&gt; select reflect(class_name,method_name,col1,col2) from test_udf2;</span><br></pre></td></tr></table></figure></p></li></ol><p><strong>需求 3: 判断是否为数字</strong></p><p><strong>实现方式:</strong></p><p>使用 <code>apache.commons</code> 中的函数，commons 下的 jar 已经包含在 hadoop 的 classpath 中，所以可以直接使用。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> reflect(<span class="string">&quot;org.apache.commons.lang.math.NumberUtils&quot;</span>, <span class="string">&quot;isNumber&quot;</span>, <span class="string">&quot;123&quot;</span>)</span><br></pre></td></tr></table></figure></p><h3 id="窗口函数与分析函数"><a href="#窗口函数与分析函数" class="headerlink" title="窗口函数与分析函数"></a>窗口函数与分析函数</h3><p>在 SQL 中有一类函数叫做聚合函数，例如 <code>sum()</code>、<code>avg()</code>、<code>max()</code> 等等，这类函数可以将多行数据按照规则聚集为一行，一般来讲聚集后的行数是要少于聚集前的行数的。但是有时我们想要既显示聚集前的数据，又要显示聚集后的数据，这时我们便引入了窗口函数。窗口函数又叫 OLAP 函数/分析函数，窗口函数兼具分组和排序功能。</p><p>窗口函数最重要的关键字是 <code>partition by</code> 和 <code>order by</code>。</p><p>具体语法如下：<code>over (partition by xxx order by xxx)</code></p><h4 id="sum、avg、min、max"><a href="#sum、avg、min、max" class="headerlink" title="sum、avg、min、max"></a>sum、avg、min、max</h4><p><strong>准备数据</strong></p><p>建表语句:<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> test_t1(</span><br><span class="line">cookieid <span class="keyword">string</span>,</span><br><span class="line">createtime <span class="keyword">string</span>,  <span class="comment">--day </span></span><br><span class="line">pv <span class="built_in">int</span></span><br><span class="line">) <span class="keyword">row</span> <span class="keyword">format</span> <span class="keyword">delimited</span> </span><br><span class="line"><span class="keyword">fields</span> <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&#x27;,&#x27;</span>;</span><br></pre></td></tr></table></figure></p><p>加载数据：<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&#x27;/root/hivedata/test_t1.dat&#x27;</span> <span class="keyword">into</span> <span class="keyword">table</span> test_t1;</span><br><span class="line"></span><br><span class="line">cookie1,2020-04-10,1</span><br><span class="line">cookie1,2020-04-11,5</span><br><span class="line">cookie1,2020-04-12,7</span><br><span class="line">cookie1,2020-04-13,3</span><br><span class="line">cookie1,2020-04-14,2</span><br><span class="line">cookie1,2020-04-15,4</span><br><span class="line">cookie1,2020-04-16,4</span><br></pre></td></tr></table></figure></p><p>开启智能本地模式<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SET</span> hive.exec.mode.local.auto=<span class="literal">true</span>;</span><br></pre></td></tr></table></figure></p><h5 id="SUM"><a href="#SUM" class="headerlink" title="SUM"></a>SUM</h5><p><code>SUM</code> 函数和窗口函数的配合使用：结果和 <code>ORDER BY</code> 相关，默认为升序。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, pv,</span><br><span class="line"><span class="keyword">SUM</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> pv1, <span class="comment">--默认为从起点到当前行</span></span><br><span class="line"><span class="keyword">SUM</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="keyword">UNBOUNDED</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span>) <span class="keyword">AS</span> pv2, <span class="comment">--从起点到当前行，结果同 pv1</span></span><br><span class="line"><span class="keyword">SUM</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid) <span class="keyword">AS</span> pv3, <span class="comment">--分组内所有行</span></span><br><span class="line"><span class="keyword">SUM</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="number">3</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span>) <span class="keyword">AS</span> pv4, <span class="comment">--当前行 + 往前 3 行</span></span><br><span class="line"><span class="keyword">SUM</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="number">3</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="number">1</span> <span class="keyword">FOLLOWING</span>) <span class="keyword">AS</span> pv5, <span class="comment">--当前行 + 往前 3 行+往后 1 行</span></span><br><span class="line"><span class="keyword">SUM</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span> <span class="keyword">AND</span> <span class="keyword">UNBOUNDED</span> <span class="keyword">FOLLOWING</span>) <span class="keyword">AS</span> pv6 <span class="comment">--当前行+往后所有行</span></span><br><span class="line"><span class="keyword">FROM</span> test_t1;</span><br><span class="line"></span><br><span class="line">cookieid createtime     pv      pv1     pv2     pv3     pv4     pv5      pv6 </span><br><span class="line"><span class="comment">-----------------------------------------------------------------------------</span></span><br><span class="line">cookie1  2015-04-10      1       1       1       26      1       6       26</span><br><span class="line">cookie1  2015-04-11      5       6       6       26      6       13      25</span><br><span class="line">cookie1  2015-04-12      7       13      13      26      13      16      20</span><br><span class="line">cookie1  2015-04-13      3       16      16      26      16      18      13</span><br><span class="line">cookie1  2015-04-14      2       18      18      26      17      21      10</span><br><span class="line">cookie1  2015-04-15      4       22      22      26      16      20      8</span><br><span class="line">cookie1  2015-04-16      4       26      26      26      13      13      4</span><br><span class="line"></span><br><span class="line">pv1: 分组内从起点到当前行的 pv 累积，如，11号的 pv1 = 10号的 pv + 11号的 pv, 12号 = 10号 + 11号 + 12号</span><br><span class="line">pv2: 同 pv1</span><br><span class="line">pv3: 分组内 (cookie1) 所有的 pv 累加</span><br><span class="line">pv4: 分组内当前行 + 往前 3 行，如，11号 = 10号 + 11号，12号 = 10号 + 11号 + 12号，13号 = 10号 + 11号 + 12号 + 13号，14号 = 11号 + 12号 + 13号 + 14号</span><br><span class="line">pv5: 分组内当前行 + 往前 3 行 + 往后 1 行，如，14号 = 11号 + 12号 + 13号 + 14号 + 15号 = 5 + 7 + 3 + 2 + 4 = 21</span><br><span class="line">pv6: 分组内当前行 + 往后所有行，如，13号 = 13号 + 14号 + 15号 + 16号 = 3 + 2 + 4 + 4 = 13， 14号 = 14号 + 15号 + 16号 = 2 + 4 + 4 = 10</span><br></pre></td></tr></table></figure></p><p>如果不指定 <code>rows between</code>，默认为从起点到当前行;</p><p>如果不指定 <code>order by</code>，则将分组内所有值累加;</p><p>关键是理解 <code>rows between</code> 含义，也叫做 <code>window</code> 子句：</p><ul><li><code>preceding</code>：往前</li><li><code>following</code>：往后</li><li><code>current row</code>：当前行</li><li><code>unbounded</code>：起点</li><li><code>unbounded preceding</code>： 表示从前面的起点</li><li><code>unbounded following</code>：表示到后面的终点</li></ul><p>AVG，MIN，MAX 和 SUM 用法一样。</p><h5 id="AVG"><a href="#AVG" class="headerlink" title="AVG"></a>AVG</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, pv,</span><br><span class="line"><span class="keyword">AVG</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> pv1, <span class="comment">--默认为从起点到当前行</span></span><br><span class="line"><span class="keyword">AVG</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="keyword">UNBOUNDED</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span>) <span class="keyword">AS</span> pv2, <span class="comment">--从起点到当前行，结果同 pv1</span></span><br><span class="line"><span class="keyword">AVG</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid) <span class="keyword">AS</span> pv3, <span class="comment">--分组内所有行</span></span><br><span class="line"><span class="keyword">AVG</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="number">3</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span>) <span class="keyword">AS</span> pv4, <span class="comment">--当前行 + 往前 3 行</span></span><br><span class="line"><span class="keyword">AVG</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="number">3</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="number">1</span> <span class="keyword">FOLLOWING</span>) <span class="keyword">AS</span> pv5, <span class="comment">--当前行 + 往前 3 行 + 往后 1 行</span></span><br><span class="line"><span class="keyword">AVG</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span> <span class="keyword">AND</span> <span class="keyword">UNBOUNDED</span> <span class="keyword">FOLLOWING</span>) <span class="keyword">AS</span> pv6 <span class="comment">--当前行 + 往后所有行</span></span><br><span class="line"><span class="keyword">FROM</span> test_t1;</span><br><span class="line"></span><br><span class="line">cookieid createtime     pv      pv1     pv2     pv3     pv4     pv5      pv6 </span><br><span class="line"><span class="comment">-----------------------------------------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10      1       1.0     1.0     3.7142857142857144      1.0     3.0     3.7142857142857144</span><br><span class="line">cookie1 2015-04-11      5       3.0     3.0     3.7142857142857144      3.0     4.333333333333333       4.166666666666667</span><br><span class="line">cookie1 2015-04-12      7       4.333333333333333       4.333333333333333       3.7142857142857144      4.333333333333333       4.0     4.0</span><br><span class="line">cookie1 2015-04-13      3       4.0     4.0     3.7142857142857144      4.0     3.6     3.25</span><br><span class="line">cookie1 2015-04-14      2       3.6     3.6     3.7142857142857144      4.25    4.2     3.3333333333333335</span><br><span class="line">cookie1 2015-04-15      4       3.6666666666666665      3.6666666666666665      3.7142857142857144      4.0     4.0     4.0</span><br><span class="line">cookie1 2015-04-16      4       3.7142857142857144      3.7142857142857144      3.7142857142857144      3.25    3.25    4.0</span><br></pre></td></tr></table></figure></p><h5 id="MIN"><a href="#MIN" class="headerlink" title="MIN"></a>MIN</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, pv,</span><br><span class="line"><span class="keyword">MIN</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> pv1, <span class="comment">--默认为从起点到当前行</span></span><br><span class="line"><span class="keyword">MIN</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="keyword">UNBOUNDED</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span>) <span class="keyword">AS</span> pv2, <span class="comment">--从起点到当前行，结果同 pv1</span></span><br><span class="line"><span class="keyword">MIN</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid) <span class="keyword">AS</span> pv3, <span class="comment">--分组内所有行</span></span><br><span class="line"><span class="keyword">MIN</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="number">3</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span>) <span class="keyword">AS</span> pv4, <span class="comment">--当前行 + 往前 3 行</span></span><br><span class="line"><span class="keyword">MIN</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="number">3</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="number">1</span> <span class="keyword">FOLLOWING</span>) <span class="keyword">AS</span> pv5, <span class="comment">--当前行 + 往前 3 行 + 往后 1 行</span></span><br><span class="line"><span class="keyword">MIN</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span> <span class="keyword">AND</span> <span class="keyword">UNBOUNDED</span> <span class="keyword">FOLLOWING</span>) <span class="keyword">AS</span> pv6 <span class="comment">--当前行 + 往后所有行</span></span><br><span class="line"><span class="keyword">FROM</span> test_t1;</span><br><span class="line"></span><br><span class="line">cookieid createtime     pv      pv1     pv2     pv3     pv4     pv5      pv6 </span><br><span class="line"><span class="comment">-----------------------------------------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10      1       1       1       1       1       1       1</span><br><span class="line">cookie1 2015-04-11      5       1       1       1       1       1       2</span><br><span class="line">cookie1 2015-04-12      7       1       1       1       1       1       2</span><br><span class="line">cookie1 2015-04-13      3       1       1       1       1       1       2</span><br><span class="line">cookie1 2015-04-14      2       1       1       1       2       2       2</span><br><span class="line">cookie1 2015-04-15      4       1       1       1       2       2       4</span><br><span class="line">cookie1 2015-04-16      4       1       1       1       2       2       4</span><br></pre></td></tr></table></figure></p><h5 id="MAX"><a href="#MAX" class="headerlink" title="MAX"></a>MAX</h5><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, pv,</span><br><span class="line"><span class="keyword">MAX</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> pv1, <span class="comment">--默认为从起点到当前行</span></span><br><span class="line"><span class="keyword">MAX</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="keyword">UNBOUNDED</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span>) <span class="keyword">AS</span> pv2, <span class="comment">--从起点到当前行，结果同 pv1</span></span><br><span class="line"><span class="keyword">MAX</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid) <span class="keyword">AS</span> pv3, <span class="comment">--分组内所有行</span></span><br><span class="line"><span class="keyword">MAX</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="number">3</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span>) <span class="keyword">AS</span> pv4, <span class="comment">--当前行 + 往前 3 行</span></span><br><span class="line"><span class="keyword">MAX</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="number">3</span> <span class="keyword">PRECEDING</span> <span class="keyword">AND</span> <span class="number">1</span> <span class="keyword">FOLLOWING</span>) <span class="keyword">AS</span> pv5, <span class="comment">--当前行 + 往前 3 行 + 往后 1 行</span></span><br><span class="line"><span class="keyword">MAX</span>(pv) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">ROWS</span> <span class="keyword">BETWEEN</span> <span class="keyword">CURRENT</span> <span class="keyword">ROW</span> <span class="keyword">AND</span> <span class="keyword">UNBOUNDED</span> <span class="keyword">FOLLOWING</span>) <span class="keyword">AS</span> pv6 <span class="comment">--当前行 + 往后所有行</span></span><br><span class="line"><span class="keyword">FROM</span> test_t1;</span><br><span class="line"></span><br><span class="line">cookieid createtime     pv      pv1     pv2     pv3     pv4     pv5      pv6 </span><br><span class="line"><span class="comment">-----------------------------------------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10      1       1       1       7       1       5       7</span><br><span class="line">cookie1 2015-04-11      5       5       5       7       5       7       7</span><br><span class="line">cookie1 2015-04-12      7       7       7       7       7       7       7</span><br><span class="line">cookie1 2015-04-13      3       7       7       7       7       7       4</span><br><span class="line">cookie1 2015-04-14      2       7       7       7       7       7       4</span><br><span class="line">cookie1 2015-04-15      4       7       7       7       7       7       4</span><br><span class="line">cookie1 2015-04-16      4       7       7       7       4       4       4</span><br></pre></td></tr></table></figure></p><h4 id="row-number、rank、dense-rank、ntile"><a href="#row-number、rank、dense-rank、ntile" class="headerlink" title="row_number、rank、dense_rank、ntile"></a>row_number、rank、dense_rank、ntile</h4><p><strong>准备数据：</strong></p><p>建表语句:</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> test_t2 (</span><br><span class="line">  cookieid <span class="keyword">string</span>,</span><br><span class="line">  createtime <span class="keyword">string</span>,  <span class="comment">--day </span></span><br><span class="line">  pv <span class="built_in">INT</span></span><br><span class="line">) <span class="keyword">ROW</span> <span class="keyword">FORMAT</span> <span class="keyword">DELIMITED</span> </span><br><span class="line"><span class="keyword">FIELDS</span> <span class="keyword">TERMINATED</span> <span class="keyword">BY</span> <span class="string">&#x27;,&#x27;</span> </span><br><span class="line"><span class="keyword">stored</span> <span class="keyword">as</span> textfile;</span><br></pre></td></tr></table></figure></p><p><strong>加载数据：</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&#x27;/root/hivedata/test_t2.dat&#x27;</span> <span class="keyword">into</span> <span class="keyword">table</span> test_t2;</span><br><span class="line"></span><br><span class="line">cookie1,2020-04-10,1</span><br><span class="line">cookie1,2020-04-11,5</span><br><span class="line">cookie1,2020-04-12,7</span><br><span class="line">cookie1,2020-04-13,3</span><br><span class="line">cookie1,2020-04-14,2</span><br><span class="line">cookie1,2020-04-15,4</span><br><span class="line">cookie1,2020-04-16,4</span><br><span class="line">cookie2,2020-04-10,2</span><br><span class="line">cookie2,2020-04-11,3</span><br><span class="line">cookie2,2020-04-12,5</span><br><span class="line">cookie2,2020-04-13,6</span><br><span class="line">cookie2,2020-04-14,3</span><br><span class="line">cookie2,2020-04-15,9</span><br><span class="line">cookie2,2020-04-16,7</span><br></pre></td></tr></table></figure></p><h5 id="ROW-NUMBER-使用"><a href="#ROW-NUMBER-使用" class="headerlink" title="ROW_NUMBER() 使用"></a>ROW_NUMBER() 使用</h5><p><code>ROW_NUMBER()</code> 从 1 开始，按照顺序，生成分组内记录的序列。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, pv,</span><br><span class="line">ROW_NUMBER() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> pv <span class="keyword">desc</span>) <span class="keyword">AS</span> rn </span><br><span class="line"><span class="keyword">FROM</span> test_t2;</span><br><span class="line"></span><br><span class="line">cookieid day           pv       rn</span><br><span class="line"><span class="comment">------------------------------------------- </span></span><br><span class="line">cookie1 2015-04-12      7       1</span><br><span class="line">cookie1 2015-04-11      5       2</span><br><span class="line">cookie1 2015-04-15      4       3</span><br><span class="line">cookie1 2015-04-16      4       4</span><br><span class="line">cookie1 2015-04-13      3       5</span><br><span class="line">cookie1 2015-04-14      2       6</span><br><span class="line">cookie1 2015-04-10      1       7</span><br><span class="line">cookie2 2015-04-15      9       1</span><br><span class="line">cookie2 2015-04-16      7       2</span><br><span class="line">cookie2 2015-04-13      6       3</span><br><span class="line">cookie2 2015-04-12      5       4</span><br><span class="line">cookie2 2015-04-14      3       5</span><br><span class="line">cookie2 2015-04-11      3       6</span><br><span class="line">cookie2 2015-04-10      2       7</span><br></pre></td></tr></table></figure></p><h5 id="RANK-和-DENSE-RANK-使用"><a href="#RANK-和-DENSE-RANK-使用" class="headerlink" title="RANK 和 DENSE_RANK 使用"></a>RANK 和 DENSE_RANK 使用</h5><ul><li><code>RANK()</code> 生成数据项在分组中的排名，排名相等会在名次中留下空位。</li><li><code>DENSE_RANK()</code> 生成数据项在分组中的排名，排名相等会在名次中不会留下空位。</li></ul><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, pv,</span><br><span class="line"><span class="keyword">RANK</span>() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> pv <span class="keyword">desc</span>) <span class="keyword">AS</span> rn1,</span><br><span class="line"><span class="keyword">DENSE_RANK</span>() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> pv <span class="keyword">desc</span>) <span class="keyword">AS</span> rn2,</span><br><span class="line">ROW_NUMBER() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> pv <span class="keyword">DESC</span>) <span class="keyword">AS</span> rn3 </span><br><span class="line"><span class="keyword">FROM</span> test_t2 </span><br><span class="line"><span class="keyword">WHERE</span> cookieid = <span class="string">&#x27;cookie1&#x27;</span>;</span><br><span class="line"></span><br><span class="line">cookieid day           pv       rn1     rn2     rn3 </span><br><span class="line"><span class="comment">-------------------------------------------------- </span></span><br><span class="line">cookie1 2015-04-12      7       1       1       1</span><br><span class="line">cookie1 2015-04-11      5       2       2       2</span><br><span class="line">cookie1 2015-04-15      4       3       3       3</span><br><span class="line">cookie1 2015-04-16      4       3       3       4</span><br><span class="line">cookie1 2015-04-13      3       5       4       5</span><br><span class="line">cookie1 2015-04-14      2       6       5       6</span><br><span class="line">cookie1 2015-04-10      1       7       6       7</span><br><span class="line"></span><br><span class="line">rn1: 15 号和 16 号并列第 3, 13 号排第 5</span><br><span class="line">rn2: 15 号和 16 号并列第 3, 13 号排第 4</span><br><span class="line">rn3: 如果相等，则按记录值排序，生成唯一的次序，如果所有记录值都相等，或许会随机排吧。</span><br></pre></td></tr></table></figure></p><h5 id="NTILE"><a href="#NTILE" class="headerlink" title="NTILE"></a>NTILE</h5><p>有时会有这样的需求：如果数据排序后分为三部分，业务人员只关心其中的一部分，如何将这中间的三分之一数据拿出来呢? <code>NTILE</code> 函数即可以满足。</p><p><code>NTILE</code> 可以看成是：把有序的数据集合平均分配到指定的数量（num）个桶中, 将桶号分配给每一行。如果不能平均分配，则优先分配较小编号的桶，并且各个桶中能放的行数最多相差 1。</p><p>然后可以根据桶号，选取前或后 n 分之几的数据。数据会完整展示出来，只是给相应的数据打标签；具体要取几分之几的数据，需要再嵌套一层根据标签取出。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, pv,</span><br><span class="line">NTILE(<span class="number">2</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> rn1, <span class="comment">--分组内将数据分成 2 片</span></span><br><span class="line">NTILE(<span class="number">3</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> rn2, <span class="comment">--分组内将数据分成 3 片</span></span><br><span class="line">NTILE(<span class="number">4</span>) <span class="keyword">OVER</span>(<span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> rn3 <span class="comment">--将所有数据分成 4 片</span></span><br><span class="line"><span class="keyword">FROM</span> test_t2 </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> cookieid, createtime;</span><br><span class="line"></span><br><span class="line">cookieid day           pv       rn1     rn2     rn3</span><br><span class="line"><span class="comment">-------------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10      1       1       1       1</span><br><span class="line">cookie1 2015-04-11      5       1       1       1</span><br><span class="line">cookie1 2015-04-12      7       1       1       2</span><br><span class="line">cookie1 2015-04-13      3       1       2       2</span><br><span class="line">cookie1 2015-04-14      2       2       2       3</span><br><span class="line">cookie1 2015-04-15      4       2       3       3</span><br><span class="line">cookie1 2015-04-16      4       2       3       4</span><br><span class="line">cookie2 2015-04-10      2       1       1       1</span><br><span class="line">cookie2 2015-04-11      3       1       1       1</span><br><span class="line">cookie2 2015-04-12      5       1       1       2</span><br><span class="line">cookie2 2015-04-13      6       1       2       2</span><br><span class="line">cookie2 2015-04-14      3       2       2       3</span><br><span class="line">cookie2 2015-04-15      9       2       3       4</span><br><span class="line">cookie2 2015-04-16      7       2       3       4</span><br><span class="line"></span><br><span class="line">比如，统计一个 cookie，pv 数最多的前 1/3 的天</span><br><span class="line">rn2 = 1 的记录，就是我们想要的结果</span><br></pre></td></tr></table></figure></p><h3 id="其他一些窗口函数"><a href="#其他一些窗口函数" class="headerlink" title="其他一些窗口函数"></a>其他一些窗口函数</h3><h4 id="lag-lead-first-value-last-value"><a href="#lag-lead-first-value-last-value" class="headerlink" title="lag, lead, first_value, last_value"></a>lag, lead, first_value, last_value</h4><p><strong>准备数据</strong></p><p>建表语句:<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> test_t3(</span><br><span class="line">cookieid <span class="keyword">string</span>,</span><br><span class="line">createtime <span class="keyword">string</span>,</span><br><span class="line"><span class="keyword">url</span> <span class="keyword">string</span></span><br><span class="line">) <span class="keyword">row</span> <span class="keyword">format</span> <span class="keyword">delimited</span> </span><br><span class="line"><span class="keyword">fields</span> <span class="keyword">terminated</span> <span class="keyword">by</span> <span class="string">&#x27;,&#x27;</span>;</span><br></pre></td></tr></table></figure></p><p>加载数据：<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&#x27;/root/hivedata/test_t3.dat&#x27;</span> <span class="keyword">into</span> <span class="keyword">table</span> test_t3;</span><br><span class="line"></span><br><span class="line">cookie1,2015-04-10 10:00:02,url2</span><br><span class="line">cookie1,2015-04-10 10:00:00,url1</span><br><span class="line">cookie1,2015-04-10 10:03:04,url3</span><br><span class="line">cookie1,2015-04-10 10:50:05,url6</span><br><span class="line">cookie1,2015-04-10 11:00:00,url7</span><br><span class="line">cookie1,2015-04-10 10:10:00,url4</span><br><span class="line">cookie1,2015-04-10 10:50:01,url5</span><br><span class="line">cookie2,2015-04-10 10:00:02,url22</span><br><span class="line">cookie2,2015-04-10 10:00:00,url11</span><br><span class="line">cookie2,2015-04-10 10:03:04,url33</span><br><span class="line">cookie2,2015-04-10 10:50:05,url66</span><br><span class="line">cookie2,2015-04-10 11:00:00,url77</span><br><span class="line">cookie2,2015-04-10 10:10:00,url44</span><br><span class="line">cookie2,2015-04-10 10:50:01,url55</span><br></pre></td></tr></table></figure></p><h5 id="LAG"><a href="#LAG" class="headerlink" title="LAG"></a>LAG</h5><p><code>LAG(col, n, DEFAULT)</code> 用于统计窗口内往上第 n 行值第一个参数为列名，第二个参数为往上第 n 行（可选，默认为 1），第三个参数为默认值（当往上第 n 行为 NULL 时候，取默认值，如不指定，则为 NULL）<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, <span class="keyword">url</span>,</span><br><span class="line">  ROW_NUMBER() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> rn,</span><br><span class="line">  LAG(createtime, <span class="number">1</span>, <span class="string">&#x27;1970-01-01 00:00:00&#x27;</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> last_1_time,</span><br><span class="line">  LAG(createtime, <span class="number">2</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> last_2_time </span><br><span class="line"><span class="keyword">FROM</span> test_t3;</span><br><span class="line"></span><br><span class="line">cookieid createtime             url    rn       last_1_time             last_2_time</span><br><span class="line"><span class="comment">-------------------------------------------------------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10 10:00:00     url1    1       1970-01-01 00:00:00     NULL</span><br><span class="line">cookie1 2015-04-10 10:00:02     url2    2       2015-04-10 10:00:00     NULL</span><br><span class="line">cookie1 2015-04-10 10:03:04     url3    3       2015-04-10 10:00:02     2015-04-10 10:00:00</span><br><span class="line">cookie1 2015-04-10 10:10:00     url4    4       2015-04-10 10:03:04     2015-04-10 10:00:02</span><br><span class="line">cookie1 2015-04-10 10:50:01     url5    5       2015-04-10 10:10:00     2015-04-10 10:03:04</span><br><span class="line">cookie1 2015-04-10 10:50:05     url6    6       2015-04-10 10:50:01     2015-04-10 10:10:00</span><br><span class="line">cookie1 2015-04-10 11:00:00     url7    7       2015-04-10 10:50:05     2015-04-10 10:50:01</span><br><span class="line">cookie2 2015-04-10 10:00:00     url11   1       1970-01-01 00:00:00     NULL</span><br><span class="line">cookie2 2015-04-10 10:00:02     url22   2       2015-04-10 10:00:00     NULL</span><br><span class="line">cookie2 2015-04-10 10:03:04     url33   3       2015-04-10 10:00:02     2015-04-10 10:00:00</span><br><span class="line">cookie2 2015-04-10 10:10:00     url44   4       2015-04-10 10:03:04     2015-04-10 10:00:02</span><br><span class="line">cookie2 2015-04-10 10:50:01     url55   5       2015-04-10 10:10:00     2015-04-10 10:03:04</span><br><span class="line">cookie2 2015-04-10 10:50:05     url66   6       2015-04-10 10:50:01     2015-04-10 10:10:00</span><br><span class="line">cookie2 2015-04-10 11:00:00     url77   7       2015-04-10 10:50:05     2015-04-10 10:50:01</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">last_1_time:</span><br><span class="line">  指定了往上第 1 行的值，default 为 &#x27;1970-01-01 00:00:00&#x27;</span><br><span class="line">  cookie1 第一行，往上 1 行为 NULL，因此取默认值 1970-01-01 00:00:00</span><br><span class="line">  cookie1 第三行，往上 1 行值为第二行值，2015-04-10 10:00:02</span><br><span class="line">  cookie1 第六行，往上 1 行值为第五行值，2015-04-10 10:50:01</span><br><span class="line"></span><br><span class="line">last_2_time: </span><br><span class="line">  指定了往上第 2 行的值，为指定默认值</span><br><span class="line">  cookie1 第一行，往上 2 行为 NULL</span><br><span class="line">  cookie1 第二行，往上 2 行为 NULL</span><br><span class="line">  cookie1 第四行，往上 2 行为第二行值，2015-04-10 10:00:02</span><br><span class="line">  cookie1 第七行，往上 2 行为第五行值，2015-04-10 10:50:01</span><br></pre></td></tr></table></figure></p><h5 id="LEAD"><a href="#LEAD" class="headerlink" title="LEAD"></a>LEAD</h5><p>与 LAG 相反 <code>LEAD(col, n, DEFAULT)</code> 用于统计窗口内往下第 n 行值第一个参数为列名，第二个参数为往下第 n 行（可选，默认为 1），第三个参数为默认值（当往下第 n 行为 NULL 时候，取默认值，如不指定，则为 NULL）</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, <span class="keyword">url</span>,</span><br><span class="line">  ROW_NUMBER() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> rn,</span><br><span class="line">  <span class="keyword">LEAD</span>(createtime, <span class="number">1</span>, <span class="string">&#x27;1970-01-01 00:00:00&#x27;</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> next_1_time,</span><br><span class="line">  <span class="keyword">LEAD</span>(createtime, <span class="number">2</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> next_2_time </span><br><span class="line"><span class="keyword">FROM</span> test_t3;</span><br><span class="line"></span><br><span class="line">cookieid createtime             url    rn       next_1_time             next_2_time </span><br><span class="line"><span class="comment">-------------------------------------------------------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10 10:00:00     url1    1       2015-04-10 10:00:02     2015-04-10 10:03:04</span><br><span class="line">cookie1 2015-04-10 10:00:02     url2    2       2015-04-10 10:03:04     2015-04-10 10:10:00</span><br><span class="line">cookie1 2015-04-10 10:03:04     url3    3       2015-04-10 10:10:00     2015-04-10 10:50:01</span><br><span class="line">cookie1 2015-04-10 10:10:00     url4    4       2015-04-10 10:50:01     2015-04-10 10:50:05</span><br><span class="line">cookie1 2015-04-10 10:50:01     url5    5       2015-04-10 10:50:05     2015-04-10 11:00:00</span><br><span class="line">cookie1 2015-04-10 10:50:05     url6    6       2015-04-10 11:00:00     NULL</span><br><span class="line">cookie1 2015-04-10 11:00:00     url7    7       1970-01-01 00:00:00     NULL</span><br><span class="line">cookie2 2015-04-10 10:00:00     url11   1       2015-04-10 10:00:02     2015-04-10 10:03:04</span><br><span class="line">cookie2 2015-04-10 10:00:02     url22   2       2015-04-10 10:03:04     2015-04-10 10:10:00</span><br><span class="line">cookie2 2015-04-10 10:03:04     url33   3       2015-04-10 10:10:00     2015-04-10 10:50:01</span><br><span class="line">cookie2 2015-04-10 10:10:00     url44   4       2015-04-10 10:50:01     2015-04-10 10:50:05</span><br><span class="line">cookie2 2015-04-10 10:50:01     url55   5       2015-04-10 10:50:05     2015-04-10 11:00:00</span><br><span class="line">cookie2 2015-04-10 10:50:05     url66   6       2015-04-10 11:00:00     NULL</span><br><span class="line">cookie2 2015-04-10 11:00:00     url77   7       1970-01-01 00:00:00     NULL</span><br><span class="line"></span><br><span class="line">逻辑与 LAG 一样，只不过 LAG 是往上，LEAD 是往下。</span><br></pre></td></tr></table></figure></p><h5 id="FIRST-VALUE"><a href="#FIRST-VALUE" class="headerlink" title="FIRST_VALUE"></a>FIRST_VALUE</h5><p>取分组内排序后，截止到当前行，第一个值</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, <span class="keyword">url</span>,</span><br><span class="line">  ROW_NUMBER() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> rn,</span><br><span class="line">  <span class="keyword">FIRST_VALUE</span>(<span class="keyword">url</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> first1 </span><br><span class="line"><span class="keyword">FROM</span> test_t3;</span><br><span class="line"></span><br><span class="line">cookieid  createtime            url     rn      first1</span><br><span class="line"><span class="comment">---------------------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10 10:00:00     url1    1       url1</span><br><span class="line">cookie1 2015-04-10 10:00:02     url2    2       url1</span><br><span class="line">cookie1 2015-04-10 10:03:04     url3    3       url1</span><br><span class="line">cookie1 2015-04-10 10:10:00     url4    4       url1</span><br><span class="line">cookie1 2015-04-10 10:50:01     url5    5       url1</span><br><span class="line">cookie1 2015-04-10 10:50:05     url6    6       url1</span><br><span class="line">cookie1 2015-04-10 11:00:00     url7    7       url1</span><br><span class="line">cookie2 2015-04-10 10:00:00     url11   1       url11</span><br><span class="line">cookie2 2015-04-10 10:00:02     url22   2       url11</span><br><span class="line">cookie2 2015-04-10 10:03:04     url33   3       url11</span><br><span class="line">cookie2 2015-04-10 10:10:00     url44   4       url11</span><br><span class="line">cookie2 2015-04-10 10:50:01     url55   5       url11</span><br><span class="line">cookie2 2015-04-10 10:50:05     url66   6       url11</span><br><span class="line">cookie2 2015-04-10 11:00:00     url77   7       url11</span><br></pre></td></tr></table></figure></p><h5 id="LAST-VALUE"><a href="#LAST-VALUE" class="headerlink" title="LAST_VALUE"></a>LAST_VALUE</h5><p>取分组内排序后，截止到当前行，最后一个值</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, <span class="keyword">url</span>,</span><br><span class="line">  ROW_NUMBER() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> rn,</span><br><span class="line">  <span class="keyword">LAST_VALUE</span>(<span class="keyword">url</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> last1 </span><br><span class="line"><span class="keyword">FROM</span> test_t3;</span><br><span class="line"></span><br><span class="line">cookieid  createtime            url    rn       last1</span><br><span class="line"><span class="comment">-----------------------------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10 10:00:00     url1    1       url1</span><br><span class="line">cookie1 2015-04-10 10:00:02     url2    2       url2</span><br><span class="line">cookie1 2015-04-10 10:03:04     url3    3       url3</span><br><span class="line">cookie1 2015-04-10 10:10:00     url4    4       url4</span><br><span class="line">cookie1 2015-04-10 10:50:01     url5    5       url5</span><br><span class="line">cookie1 2015-04-10 10:50:05     url6    6       url6</span><br><span class="line">cookie1 2015-04-10 11:00:00     url7    7       url7</span><br><span class="line">cookie2 2015-04-10 10:00:00     url11   1       url11</span><br><span class="line">cookie2 2015-04-10 10:00:02     url22   2       url22</span><br><span class="line">cookie2 2015-04-10 10:03:04     url33   3       url33</span><br><span class="line">cookie2 2015-04-10 10:10:00     url44   4       url44</span><br><span class="line">cookie2 2015-04-10 10:50:01     url55   5       url55</span><br><span class="line">cookie2 2015-04-10 10:50:05     url66   6       url66</span><br><span class="line">cookie2 2015-04-10 11:00:00     url77   7       url77</span><br></pre></td></tr></table></figure></p><p>如果想要取分组内排序后最后一个值，则需要变通一下：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, <span class="keyword">url</span>,</span><br><span class="line">  ROW_NUMBER() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> rn,</span><br><span class="line">  <span class="keyword">LAST_VALUE</span>(<span class="keyword">url</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime) <span class="keyword">AS</span> last1,</span><br><span class="line">  <span class="keyword">FIRST_VALUE</span>(<span class="keyword">url</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid <span class="keyword">ORDER</span> <span class="keyword">BY</span> createtime <span class="keyword">DESC</span>) <span class="keyword">AS</span> last2 </span><br><span class="line"><span class="keyword">FROM</span> test_t3 </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> cookieid, createtime;</span><br><span class="line"></span><br><span class="line">cookieid  createtime            url     rn     last1    last2</span><br><span class="line"><span class="comment">-------------------------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10 10:00:00     url1    1       url1    url7</span><br><span class="line">cookie1 2015-04-10 10:00:02     url2    2       url2    url7</span><br><span class="line">cookie1 2015-04-10 10:03:04     url3    3       url3   url7</span><br><span class="line">cookie1 2015-04-10 10:10:00     url4    4       url4    url7</span><br><span class="line">cookie1 2015-04-10 10:50:01     url5    5       url5    url7</span><br><span class="line">cookie1 2015-04-10 10:50:05     url6    6       url6    url7</span><br><span class="line">cookie1 2015-04-10 11:00:00     url7    7       url7    url7</span><br><span class="line">cookie2 2015-04-10 10:00:00     url11   1       url11   url77</span><br><span class="line">cookie2 2015-04-10 10:00:02     url22   2       url22   url77</span><br><span class="line">cookie2 2015-04-10 10:03:04     url33   3       url33   url77</span><br><span class="line">cookie2 2015-04-10 10:10:00     url44   4       url44   url77</span><br><span class="line">cookie2 2015-04-10 10:50:01     url55   5       url55   url77</span><br><span class="line">cookie2 2015-04-10 10:50:05     url66   6       url66   url77</span><br><span class="line">cookie2 2015-04-10 11:00:00     url77   7       url77   url77</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>在使用分析函数的过程中特别注意 <code>ORDER BY</code>， 如果不指定 <code>ORDER BY</code>，则默认按照记录在文件中的偏移量进行排序，会出现错误的结果</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, <span class="keyword">url</span>,</span><br><span class="line">  <span class="keyword">FIRST_VALUE</span>(<span class="keyword">url</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid) <span class="keyword">AS</span> first2</span><br><span class="line"><span class="keyword">FROM</span> test_t3;</span><br><span class="line"></span><br><span class="line">cookieid  createtime            url     first2</span><br><span class="line"><span class="comment">----------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10 10:00:02     url2    url2</span><br><span class="line">cookie1 2015-04-10 10:00:00     url1    url2</span><br><span class="line">cookie1 2015-04-10 10:03:04     url3    url2</span><br><span class="line">cookie1 2015-04-10 10:50:05     url6    url2</span><br><span class="line">cookie1 2015-04-10 11:00:00     url7    url2</span><br><span class="line">cookie1 2015-04-10 10:10:00     url4    url2</span><br><span class="line">cookie1 2015-04-10 10:50:01     url5    url2</span><br><span class="line">cookie2 2015-04-10 10:00:02     url22   url22</span><br><span class="line">cookie2 2015-04-10 10:00:00     url11   url22</span><br><span class="line">cookie2 2015-04-10 10:03:04     url33   url22</span><br><span class="line">cookie2 2015-04-10 10:50:05     url66   url22</span><br><span class="line">cookie2 2015-04-10 11:00:00     url77   url22</span><br><span class="line">cookie2 2015-04-10 10:10:00     url44   url22</span><br><span class="line">cookie2 2015-04-10 10:50:01     url55   url22</span><br><span class="line"></span><br><span class="line"><span class="keyword">SELECT</span> cookieid, createtime, <span class="keyword">url</span>,</span><br><span class="line">  <span class="keyword">LAST_VALUE</span>(<span class="keyword">url</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> cookieid) <span class="keyword">AS</span> last2</span><br><span class="line"><span class="keyword">FROM</span> test_t3;</span><br><span class="line"></span><br><span class="line">cookieid  createtime            url     last2</span><br><span class="line"><span class="comment">----------------------------------------------</span></span><br><span class="line">cookie1 2015-04-10 10:00:02     url2    url5</span><br><span class="line">cookie1 2015-04-10 10:00:00     url1    url5</span><br><span class="line">cookie1 2015-04-10 10:03:04     url3    url5</span><br><span class="line">cookie1 2015-04-10 10:50:05     url6    url5</span><br><span class="line">cookie1 2015-04-10 11:00:00     url7    url5</span><br><span class="line">cookie1 2015-04-10 10:10:00     url4    url5</span><br><span class="line">cookie1 2015-04-10 10:50:01     url5    url5</span><br><span class="line">cookie2 2015-04-10 10:00:02     url22   url55</span><br><span class="line">cookie2 2015-04-10 10:00:00     url11   url55</span><br><span class="line">cookie2 2015-04-10 10:03:04     url33   url55</span><br><span class="line">cookie2 2015-04-10 10:50:05     url66   url55</span><br><span class="line">cookie2 2015-04-10 11:00:00     url77   url55</span><br><span class="line">cookie2 2015-04-10 10:10:00     url44   url55</span><br><span class="line">cookie2 2015-04-10 10:50:01     url55   url55</span><br></pre></td></tr></table></figure></p><h4 id="cume-dist-percent-rank"><a href="#cume-dist-percent-rank" class="headerlink" title="cume_dist, percent_rank"></a>cume_dist, percent_rank</h4><p>这两个序列分析函数不是很常用，<strong>注意：</strong>序列函数不支持 WINDOW 子句</p><p><strong>数据准备</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">d1,user1,1000</span><br><span class="line">d1,user2,2000</span><br><span class="line">d1,user3,3000</span><br><span class="line">d2,user4,4000</span><br><span class="line">d2,user5,5000</span><br><span class="line"></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">EXTERNAL</span> <span class="keyword">TABLE</span> test_t4 (</span><br><span class="line">  dept <span class="keyword">STRING</span>,</span><br><span class="line">  userid <span class="keyword">string</span>,</span><br><span class="line">  sal <span class="built_in">INT</span></span><br><span class="line">) <span class="keyword">ROW</span> <span class="keyword">FORMAT</span> <span class="keyword">DELIMITED</span> </span><br><span class="line"><span class="keyword">FIELDS</span> <span class="keyword">TERMINATED</span> <span class="keyword">BY</span> <span class="string">&#x27;,&#x27;</span> </span><br><span class="line"><span class="keyword">stored</span> <span class="keyword">as</span> textfile;</span><br></pre></td></tr></table></figure></p><p><strong>加载数据：</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&#x27;/root/hivedata/test_t4.dat&#x27;</span> <span class="keyword">into</span> <span class="keyword">table</span> test_t4;</span><br></pre></td></tr></table></figure></p><h5 id="CUME-DIST"><a href="#CUME-DIST" class="headerlink" title="CUME_DIST"></a>CUME_DIST</h5><p><code>CUME_DIST</code> 和 <code>order by</code> 的排序顺序有关系</p><p><code>CUME_DIST</code> 小于等于当前值的行数/分组内总行数 <code>order</code> 默认顺序（正序 升序）比如，统计小于等于当前薪水的人数，所占总人数的比例</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> dept, userid, sal,</span><br><span class="line">  <span class="keyword">CUME_DIST</span>() <span class="keyword">OVER</span>(<span class="keyword">ORDER</span> <span class="keyword">BY</span> sal) <span class="keyword">AS</span> rn1,</span><br><span class="line">  <span class="keyword">CUME_DIST</span>() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> dept <span class="keyword">ORDER</span> <span class="keyword">BY</span> sal) <span class="keyword">AS</span> rn2</span><br><span class="line"><span class="keyword">FROM</span> test_t4;</span><br><span class="line"></span><br><span class="line">dept    userid   sal   rn1       rn2 </span><br><span class="line"><span class="comment">-------------------------------------------</span></span><br><span class="line">d1      user1   1000    0.2     0.3333333333333333</span><br><span class="line">d1      user2   2000    0.4     0.6666666666666666</span><br><span class="line">d1      user3   3000    0.6     1.0</span><br><span class="line">d2      user4   4000    0.8     0.5</span><br><span class="line">d2      user5   5000    1.0     1.0</span><br><span class="line"> </span><br><span class="line">rn1: 没有 partition，所有数据均为 1 组，总行数为 5，</span><br><span class="line">     第一行：小于等于 1000 的行数为 1，因此，1/5=0.2</span><br><span class="line">     第三行：小于等于 3000 的行数为 3，因此，3/5=0.6</span><br><span class="line">rn2: 按照部门分组，dpet=d1 的行数为 3，</span><br><span class="line">     第二行：小于等于 2000 的行数为 2，因此，2/3=0.6666666666666666</span><br></pre></td></tr></table></figure></p><h5 id="PERCENT-RANK"><a href="#PERCENT-RANK" class="headerlink" title="PERCENT_RANK"></a>PERCENT_RANK</h5><p><code>PERCENT_RANK</code> 分组内当前行的 (RANK 值 - 1)/(分组内总行数 - 1)</p><p>经调研，该函数显示现实意义不明朗，有待于继续考证</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> dept, userid, sal,</span><br><span class="line">  <span class="keyword">PERCENT_RANK</span>() <span class="keyword">OVER</span>(<span class="keyword">ORDER</span> <span class="keyword">BY</span> sal) <span class="keyword">AS</span> rn1,   <span class="comment">--分组内</span></span><br><span class="line">  <span class="keyword">RANK</span>() <span class="keyword">OVER</span>(<span class="keyword">ORDER</span> <span class="keyword">BY</span> sal) <span class="keyword">AS</span> rn11,          <span class="comment">--分组内 RANK 值</span></span><br><span class="line">  <span class="keyword">SUM</span>(<span class="number">1</span>) <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> <span class="literal">NULL</span>) <span class="keyword">AS</span> rn12,     <span class="comment">--分组内总行数</span></span><br><span class="line">  <span class="keyword">PERCENT_RANK</span>() <span class="keyword">OVER</span>(<span class="keyword">PARTITION</span> <span class="keyword">BY</span> dept <span class="keyword">ORDER</span> <span class="keyword">BY</span> sal) <span class="keyword">AS</span> rn2 </span><br><span class="line"><span class="keyword">FROM</span> test_t4;</span><br><span class="line"></span><br><span class="line">dept    userid   sal    rn1    rn11     rn12    rn2</span><br><span class="line"><span class="comment">---------------------------------------------------</span></span><br><span class="line">d1      user1   1000    0.0     1       5       0.0</span><br><span class="line">d1      user2   2000    0.25    2       5       0.5</span><br><span class="line">d1      user3   3000    0.5     3       5       1.0</span><br><span class="line">d2      user4   4000    0.75    4       5       0.0</span><br><span class="line">d2      user5   5000    1.0     5       5       1.0</span><br><span class="line"></span><br><span class="line">rn1: rn1 = (rn11-1) / (rn12-1) </span><br><span class="line">  第一行，(1-1)/(5-1) = 0/4 = 0</span><br><span class="line">  第二行，(2-1)/(5-1) = 1/4 = 0.25</span><br><span class="line">  第四行，(4-1)/(5-1) = 3/4 = 0.75</span><br><span class="line">rn2: 按照 dept 分组，</span><br><span class="line">  dept=d1 的总行数为 3</span><br><span class="line">  第一行，(1-1)/(3-1) = 0</span><br><span class="line">  第三行，(3-1)/(3-1) = 1</span><br></pre></td></tr></table></figure></p><h4 id="grouping-sets-cube-rollup"><a href="#grouping-sets-cube-rollup" class="headerlink" title="grouping sets, cube, rollup"></a>grouping sets, cube, rollup</h4><p>这几个分析函数通常用于 OLAP 中，不能累加，而且需要根据不同维度上钻和下钻的指标统计，比如，分小时、天、月的 UV 数。</p><p><strong>数据准备：</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> test_t5 (</span><br><span class="line">  <span class="keyword">month</span> <span class="keyword">STRING</span>,</span><br><span class="line">  <span class="keyword">day</span> <span class="keyword">STRING</span>, </span><br><span class="line">  cookieid <span class="keyword">STRING</span> </span><br><span class="line">) <span class="keyword">ROW</span> <span class="keyword">FORMAT</span> <span class="keyword">DELIMITED</span> </span><br><span class="line"><span class="keyword">FIELDS</span> <span class="keyword">TERMINATED</span> <span class="keyword">BY</span> <span class="string">&#x27;,&#x27;</span> </span><br><span class="line"><span class="keyword">stored</span> <span class="keyword">as</span> textfile;</span><br></pre></td></tr></table></figure></p><p><strong>加载数据：</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">load</span> <span class="keyword">data</span> <span class="keyword">local</span> inpath <span class="string">&#x27;/root/hivedata/test_t5.dat&#x27;</span> <span class="keyword">into</span> <span class="keyword">table</span> test_t5;</span><br><span class="line"></span><br><span class="line">2020-03,2020-03-10,cookie1</span><br><span class="line">2020-03,2020-03-10,cookie5</span><br><span class="line">2020-03,2020-03-12,cookie7</span><br><span class="line">2020-04,2020-04-12,cookie3</span><br><span class="line">2020-04,2020-04-13,cookie2</span><br><span class="line">2020-04,2020-04-13,cookie4</span><br><span class="line">2020-04,2020-04-16,cookie4</span><br><span class="line">2020-03,2020-03-10,cookie2</span><br><span class="line">2020-03,2020-03-10,cookie3</span><br><span class="line">2020-04,2020-04-12,cookie5</span><br><span class="line">2020-04,2020-04-13,cookie6</span><br><span class="line">2020-04,2020-04-15,cookie3</span><br><span class="line">2020-04,2020-04-15,cookie2</span><br><span class="line">2020-04,2020-04-16,cookie1</span><br></pre></td></tr></table></figure></p><h5 id="GROUPING-SETS"><a href="#GROUPING-SETS" class="headerlink" title="GROUPING SETS"></a>GROUPING SETS</h5><p><code>grouping sets</code> 是一种将多个 <code>group by</code> 逻辑写在一个 SQL 语句中的便利写法。</p><p>等价于将不同维度的 <code>GROUP BY</code> 结果集进行 <code>UNION ALL</code>。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">month</span>, <span class="keyword">day</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, GROUPING__ID </span><br><span class="line"><span class="keyword">FROM</span> test_t5 </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">month</span>, <span class="keyword">day</span> </span><br><span class="line"><span class="keyword">GROUPING</span> <span class="keyword">SETS</span> (<span class="keyword">month</span>, <span class="keyword">day</span>) </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> GROUPING__ID;</span><br><span class="line"></span><br><span class="line">month      day            uv      GROUPING__ID</span><br><span class="line"><span class="comment">------------------------------------------------</span></span><br><span class="line">2015-03    NULL            5       1</span><br><span class="line">2015-04    NULL            6       1</span><br><span class="line">NULL       2015-03-10      4       2</span><br><span class="line">NULL       2015-03-12      1       2</span><br><span class="line">NULL       2015-04-12      2       2</span><br><span class="line">NULL       2015-04-13      3       2</span><br><span class="line">NULL       2015-04-15      2       2</span><br><span class="line">NULL       2015-04-16      2       2</span><br></pre></td></tr></table></figure></p><p><code>grouping_id</code> 表示这一组结果属于哪个分组集合，根据 <code>grouping sets</code> 中的分组条件 month，day，1 是代表 month，2 是代表 day</p><p>等价于</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">month</span>, <span class="literal">NULL</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, <span class="number">1</span> <span class="keyword">AS</span> GROUPING__ID <span class="keyword">FROM</span> test_t5 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">month</span> <span class="keyword">UNION</span> <span class="keyword">ALL</span> </span><br><span class="line"><span class="keyword">SELECT</span> <span class="literal">NULL</span> <span class="keyword">as</span> <span class="keyword">month</span>, <span class="keyword">day</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, <span class="number">2</span> <span class="keyword">AS</span> GROUPING__ID <span class="keyword">FROM</span> test_t5 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">day</span>;</span><br></pre></td></tr></table></figure></p><p>再如：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">month</span>, <span class="keyword">day</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, GROUPING__ID </span><br><span class="line"><span class="keyword">FROM</span> test_t5 </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">month</span>,<span class="keyword">day</span> </span><br><span class="line"><span class="keyword">GROUPING</span> <span class="keyword">SETS</span> (<span class="keyword">month</span>, <span class="keyword">day</span>, (<span class="keyword">month</span>, <span class="keyword">day</span>)) </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> GROUPING__ID;</span><br><span class="line"></span><br><span class="line">month         day             uv      GROUPING__ID</span><br><span class="line"><span class="comment">------------------------------------------------</span></span><br><span class="line">2015-03       NULL            5       1</span><br><span class="line">2015-04       NULL            6       1</span><br><span class="line">NULL          2015-03-10      4       2</span><br><span class="line">NULL          2015-03-12      1       2</span><br><span class="line">NULL          2015-04-12      2       2</span><br><span class="line">NULL          2015-04-13      3       2</span><br><span class="line">NULL          2015-04-15      2       2</span><br><span class="line">NULL          2015-04-16      2       2</span><br><span class="line">2015-03       2015-03-10      4       3</span><br><span class="line">2015-03       2015-03-12      1       3</span><br><span class="line">2015-04       2015-04-12      2       3</span><br><span class="line">2015-04       2015-04-13      3       3</span><br><span class="line">2015-04       2015-04-15      2       3</span><br><span class="line">2015-04       2015-04-16      2       3</span><br></pre></td></tr></table></figure></p><p>等价于</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">month</span>, <span class="literal">NULL</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, <span class="number">1</span> <span class="keyword">AS</span> GROUPING__ID <span class="keyword">FROM</span> test_t5 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">month</span> </span><br><span class="line"><span class="keyword">UNION</span> <span class="keyword">ALL</span> </span><br><span class="line"><span class="keyword">SELECT</span> <span class="literal">NULL</span>, <span class="keyword">day</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, <span class="number">2</span> <span class="keyword">AS</span> GROUPING__ID <span class="keyword">FROM</span> test_t5 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">day</span></span><br><span class="line"><span class="keyword">UNION</span> <span class="keyword">ALL</span> </span><br><span class="line"><span class="keyword">SELECT</span> <span class="keyword">month</span>, <span class="keyword">day</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, <span class="number">3</span> <span class="keyword">AS</span> GROUPING__ID <span class="keyword">FROM</span> test_t5 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">month</span>, <span class="keyword">day</span>;</span><br></pre></td></tr></table></figure></p><h5 id="CUBE"><a href="#CUBE" class="headerlink" title="CUBE"></a>CUBE</h5><p>根据 <code>GROUP BY</code> 的维度的所有组合进行聚合。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">month</span>, <span class="keyword">day</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, GROUPING__ID </span><br><span class="line"><span class="keyword">FROM</span> test_t5 </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">month</span>, <span class="keyword">day</span> </span><br><span class="line"><span class="keyword">WITH</span> <span class="keyword">CUBE</span> </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> GROUPING__ID;</span><br><span class="line"></span><br><span class="line">month           day             uv     GROUPING__ID</span><br><span class="line"><span class="comment">--------------------------------------------</span></span><br><span class="line">NULL            NULL            7       0</span><br><span class="line">2015-03         NULL            5       1</span><br><span class="line">2015-04         NULL            6       1</span><br><span class="line">NULL            2015-04-12      2       2</span><br><span class="line">NULL            2015-04-13      3       2</span><br><span class="line">NULL            2015-04-15      2       2</span><br><span class="line">NULL            2015-04-16      2       2</span><br><span class="line">NULL            2015-03-10      4       2</span><br><span class="line">NULL            2015-03-12      1       2</span><br><span class="line">2015-03         2015-03-10      4       3</span><br><span class="line">2015-03         2015-03-12      1       3</span><br><span class="line">2015-04         2015-04-16      2       3</span><br><span class="line">2015-04         2015-04-12      2       3</span><br><span class="line">2015-04         2015-04-13      3       3</span><br><span class="line">2015-04         2015-04-15      2       3</span><br></pre></td></tr></table></figure></p><p>等价于</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="literal">NULL</span>, <span class="literal">NULL</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, <span class="number">0</span> <span class="keyword">AS</span> GROUPING__ID <span class="keyword">FROM</span> test_t5</span><br><span class="line"><span class="keyword">UNION</span> <span class="keyword">ALL</span> </span><br><span class="line"><span class="keyword">SELECT</span> <span class="keyword">month</span>, <span class="literal">NULL</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, <span class="number">1</span> <span class="keyword">AS</span> GROUPING__ID <span class="keyword">FROM</span> test_t5 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">month</span> </span><br><span class="line"><span class="keyword">UNION</span> <span class="keyword">ALL</span> </span><br><span class="line"><span class="keyword">SELECT</span> <span class="literal">NULL</span>, <span class="keyword">day</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, <span class="number">2</span> <span class="keyword">AS</span> GROUPING__ID <span class="keyword">FROM</span> test_t5 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">day</span></span><br><span class="line"><span class="keyword">UNION</span> <span class="keyword">ALL</span> </span><br><span class="line"><span class="keyword">SELECT</span> <span class="keyword">month</span>, <span class="keyword">day</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, <span class="number">3</span> <span class="keyword">AS</span> GROUPING__ID <span class="keyword">FROM</span> test_t5 <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">month</span>, <span class="keyword">day</span>;</span><br></pre></td></tr></table></figure></p><h5 id="ROLLUP"><a href="#ROLLUP" class="headerlink" title="ROLLUP"></a>ROLLUP</h5><p>是 <code>CUBE</code> 的子集，以最左侧的维度为主，从该维度进行层级聚合。</p><p>比如，以 <code>month</code> 维度进行层级聚合：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">month</span>, <span class="keyword">day</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, GROUPING__ID </span><br><span class="line"><span class="keyword">FROM</span> test_t5 </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">month</span>, <span class="keyword">day</span></span><br><span class="line"><span class="keyword">WITH</span> <span class="keyword">ROLLUP</span> </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> GROUPING__ID;</span><br><span class="line"></span><br><span class="line">month            day             uv     GROUPING__ID</span><br><span class="line"><span class="comment">---------------------------------------------------</span></span><br><span class="line">NULL             NULL            7       0</span><br><span class="line">2015-03          NULL            5       1</span><br><span class="line">2015-04          NULL            6       1</span><br><span class="line">2015-03          2015-03-10      4       3</span><br><span class="line">2015-03          2015-03-12      1       3</span><br><span class="line">2015-04          2015-04-12      2       3</span><br><span class="line">2015-04          2015-04-13      3       3</span><br><span class="line">2015-04          2015-04-15      2       3</span><br><span class="line">2015-04          2015-04-16      2       3</span><br></pre></td></tr></table></figure></p><p>把 <code>month</code> 和 <code>day</code> 调换顺序，则以 <code>day</code> 维度进行层级聚合：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">day</span>, <span class="keyword">month</span>, <span class="keyword">COUNT</span>(<span class="keyword">DISTINCT</span> cookieid) <span class="keyword">AS</span> uv, GROUPING__ID </span><br><span class="line"><span class="keyword">FROM</span> test_t5 </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">day</span>, <span class="keyword">month</span> </span><br><span class="line"><span class="keyword">WITH</span> <span class="keyword">ROLLUP</span> </span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> GROUPING__ID;</span><br><span class="line"></span><br><span class="line">day             month              uv     GROUPING__ID</span><br><span class="line"><span class="comment">-------------------------------------------------------</span></span><br><span class="line">NULL            NULL               7       0</span><br><span class="line">2015-04-13      NULL               3       1</span><br><span class="line">2015-03-12      NULL               1       1</span><br><span class="line">2015-04-15      NULL               2       1</span><br><span class="line">2015-03-10      NULL               4       1</span><br><span class="line">2015-04-16      NULL               2       1</span><br><span class="line">2015-04-12      NULL               2       1</span><br><span class="line">2015-04-12      2015-04            2       3</span><br><span class="line">2015-03-10      2015-03            4       3</span><br><span class="line">2015-03-12      2015-03            1       3</span><br><span class="line">2015-04-13      2015-04            3       3</span><br><span class="line">2015-04-15      2015-04            2       3</span><br><span class="line">2015-04-16      2015-04            2       3</span><br></pre></td></tr></table></figure></p><p>这里，根据天和月进行聚合，和根据天聚合结果一样，因为有父子关系，如果是其他维度组合的话，就会不一样。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Hive/">Hive</category>
      
      
      <comments>https://blog.eurkon.com/post/7891b2e6.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>HBase 面试题解析</title>
      <link>https://blog.eurkon.com/post/47457d03.html</link>
      <guid>https://blog.eurkon.com/post/47457d03.html</guid>
      <pubDate>Fri, 02 Apr 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;HBase-的特点是什么？&quot;&gt;&lt;a href=&quot;#HBase-的特点是什么？&quot; class=&quot;headerlink&quot; title=&quot;HBase 的特点是什么？&quot;&gt;&lt;/a&gt;HBase 的特点是什么？&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;大：一个表可以有数十亿行，上百万列；&lt;/</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="HBase-的特点是什么？"><a href="#HBase-的特点是什么？" class="headerlink" title="HBase 的特点是什么？"></a>HBase 的特点是什么？</h2><ul><li>大：一个表可以有数十亿行，上百万列；</li><li>无模式：每行都有一个可排序的主键和任意多的列，列可以根据需要动态的增加，同一张表中不同的行可以有截然不同的列；</li><li>面向列：面向列（族）的存储和权限控制，列（族）独立检索；</li><li>稀疏：空（null）列并不占用存储空间，表可以设计的非常稀疏；</li><li>数据多版本：每个单元中的数据可以有多个版本，默认情况下版本号自动分配，是单元格插入时的时间戳；</li><li>数据类型单一：HBase 中的数据都是字符串，没有类型。</li></ul><h2 id="HBase-和-Hive-的区别？"><a href="#HBase-和-Hive-的区别？" class="headerlink" title="HBase 和 Hive 的区别？"></a>HBase 和 Hive 的区别？</h2><div class="table-container"><table><thead><tr><th>&nbsp;</th><th>HBase</th><th>Hive</th></tr></thead><tbody><tr><td>类型</td><td>列式数据库</td><td>数据仓库</td></tr><tr><td>内部机制</td><td>数据库引擎</td><td>MapReduce</td></tr><tr><td>增删改查</td><td>都支持</td><td>只支持导入和查询</td></tr><tr><td>Schema</td><td>只需要预先定义列族，不需要具体到列，列可以动态修改</td><td>需要预先定义表格</td></tr><tr><td>应用场景</td><td>实时</td><td>离线处理</td></tr><tr><td>特点</td><td>以 K-V 形式存储</td><td>类 SQL</td></tr></tbody></table></div><p>Hive 和 HBase 是两种基于 Hadoop 的不同技术，Hive 是一种类 SQL 的引擎，并且运行 MapReduce 任务，HBase 是一种在 Hadoop 之上的 NoSQL 的 key/value 数据库。 当然，这两种工具是可以同时使用的。就像用 Google 来搜索，用 FaceBook 进行社交一样，Hive 可以用来进行统计查询，HBase 可以用来进行实时查询，数据可以从 Hive 写到 HBase，也可以从 HBase 写回 Hive。</p><h2 id="HBase-适用于怎样的情景？"><a href="#HBase-适用于怎样的情景？" class="headerlink" title="HBase 适用于怎样的情景？"></a>HBase 适用于怎样的情景？</h2><ul><li><p>半结构化或非结构化数据：</p><p>对于数据结构字段不够确定或杂乱无章很难按一个概念去进行抽取的数据适合用 HBase。以上面的例子为例，当业务发展需要存储 author 的 email，phone，address 信息时 RDBMS 需要停机维护，而 HBase 支持动态增加。</p></li><li><p>记录非常稀疏：</p><p>RDBMS 的行有多少列是固定的，为 null 的列浪费了存储空间。而如上文提到的，HBase 为 null 的 Column 不会被存储，这样既节省了空间又提高了读性能。</p></li><li><p>多版本数据</p><p>根据 Row key 和 Column key 定位到的 value 可以有任意数量的版本值，因此对于需要存储变动历史记录的数据，用 HBase 就非常方便了。比如上例中的 author 的 address 是会变动的，业务上一般只需要最新的值，但有时可能需要查询到历史值。</p></li><li><p>超大数据量</p><p>当数据量越来越大，RDBMS 数据库撑不住了，就出现了读写分离策略，通过一个 Master 专门负责写操作，多个 Slave 负责读操作，服务器成本倍增。随着压力增加，Master 撑不住了，这时就要分库了，把关联不大的数据分开部署，一些 join 查询不能用了，需要借助中间层。随着数据量的进一步增加，一个表的记录越来越大，查询就变得很慢，于是又得搞分表，比如按 ID 取模分成多个表以减少单个表的记录数。经历过这些事的人都知道过程是多么的折腾。采用 HBase 就简单了，只需要加机器即可，HBase 会自动水平切分扩展，跟 Hadoop 的无缝集成保障了其数据可靠性（HDFS）和海量数据分析的高性能（MapReduce）。</p></li></ul><h2 id="HBase-的架构体系？"><a href="#HBase-的架构体系？" class="headerlink" title="HBase 的架构体系？"></a>HBase 的架构体系？</h2><p><strong>架构体系</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hbase_architecture.png" alt="HBase 架构体系"></p><ul><li>Zookeeper：作为分布式的协调。RegionServer 也会把自己的信息写到 Zookeeper 中。</li><li>HDFS 是 HBase 运行的底层文件系统。</li><li>RegionServer：理解为数据节点，存储数据的。</li><li>Master RegionServer：要实时的向 Master 报告信息。Master 知道全局的 RegionServer 运行情况，可以控制 RegionServer 的故障转移和 Region 的切分。</li></ul><p><strong>架构细化</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hbase_deep_architecture.png" alt="HBase 架构细化"></p><ul><li>HMaster 是 Master Server 的实现，负责监控集群中的 RegionServer 实例，同时是所有 metadata 改变的接口，在集群中，通常运行在 NameNode 上面。<ul><li>HMasterInterface 暴露的接口：Table(createTable，modifyTable，removeTable，enable，disable)，ColumnFamily (addColumn，modifyColumn，removeColumn)，Region (move，assign，unassign)。</li><li>Master 运行的后台线程：<ul><li>LoadBalancer 线程，控制 region 来平衡集群的负载。</li><li>CatalogJanitor 线程，周期性的检查 hbase:meta 表。</li></ul></li></ul></li><li>HRegionServer 是 RegionServer 的实现，服务和管理 Regions，集群中 RegionServer 运行在 DataNode。<ul><li>HRegionRegionInterface 暴露接口：Data(get，put，delete，next，etc.)，Region(splitRegion，compactRegion，etc.)。</li><li>RegionServer 后台线程：CompactSplitThread，MajorCompactionChecker，MemStoreFlusher，LogRoller。</li></ul></li><li>Regions：代表 table，Region 有多个 Store（列簇），Store 有一个 MemStore 和多个 StoreFiles（HFile），StoreFiles 的底层是 Block。</li></ul><h2 id="描述-HBase-的-RowKey-的设计原则？"><a href="#描述-HBase-的-RowKey-的设计原则？" class="headerlink" title="描述 HBase 的 RowKey 的设计原则？"></a>描述 HBase 的 RowKey 的设计原则？</h2><ul><li><p>RowKey 长度原则：RowKey 是一个二进制码流，RowKey 的长度被很多开发者建议说设计在 10~100 个字节，不过建议是越短越好，不要超过 16 个字节。原因如下：</p><ul><li>数据的持久化文件 HFile 中是按照 KeyValue 存储的，如果 RowKey 过长比如 100 个字节，1000 万列数据光 RowKey 就要占用 100*1000 万 = 10 亿个字节， 将近 1GB 数据，这会极大影响 HFile 的存储效率；</li><li>MemStore 将缓存部分数据到内存，如果 RowKey 字段过长内存的有效利用率会降低，系统将无法缓存更多的数据，这会降低检索效率。因此 RowKey 的字节长度越短越好。</li><li>目前操作系统是都是 64 位系统，内存 8 字节对齐。控制在 16 个字节，8 字节的整数倍利用操作系统的最佳特性。</li></ul></li><li><p>RowKey 散列原则</p><p>如果 RowKey 是按时间戳的方式递增，不要将时间放在二进制码的前面，建议将 RowKey 的高位作为散列字段，由程序循环生成，低位放时间字段，这样将提高数据均衡分布在每个 RegionServer 实现负载均衡的几率。如果没有散列字段，首字段直接是时间信息将产生所有新数据都在一个 RegionServer 上堆积的热点现象，这样在做数据检索的时候负载将会集中在个别 RegionServer，降低查询效率。</p></li><li><p>RowKey 唯一原则</p><p>必须在设计上保证其唯一性。</p></li></ul><h2 id="描述-HBase-中-scan-和-get-的功能以及实现的异同？"><a href="#描述-HBase-中-scan-和-get-的功能以及实现的异同？" class="headerlink" title="描述 HBase 中 scan 和 get 的功能以及实现的异同？"></a>描述 HBase 中 <code>scan</code> 和 <code>get</code> 的功能以及实现的异同？</h2><p>HBase 的查询实现只提供两种方式：</p><ul><li><p>按指定 RowKey 获取唯一一条记录，<code>get</code> 方法（org.apache.hadoop.HBase.client.Get）处理分两种：设置了 <code>ClosestRowBefore</code> 和 没有设置 <code>ClosestRowBefore</code> 的 rowlock。主要是用来保证行的事务性，即每个 <code>get</code> 是以一个 row 来标记的。一个 row 中可以有很多 family 和 column。</p></li><li><p>按指定的条件获取一批记录，<code>scan</code> 方法（org.apache.Hadoop.HBase.client.Scan）实现条件查询功能使用的就是 <code>scan</code> 方式。</p><ul><li>scan 可以通过 <code>setCaching</code> 与 <code>setBatch</code> 方法提高速度（以空间换时间）；</li><li>scan 可以通过 <code>setStartRow</code> 与 <code>setEndRow</code> 来限定范围（<code>[start，end]</code>）start 是闭区间，end 是开区间）。范围越小，性能越高。</li><li>scan 可以通过 <code>setFilter</code> 方法添加过滤器，这也是分页、多条件查询的基础。</li></ul></li></ul><h2 id="请描述-HBase-中-scan-对象的-setCache-和-setBatch-方法的使用？"><a href="#请描述-HBase-中-scan-对象的-setCache-和-setBatch-方法的使用？" class="headerlink" title="请描述 HBase 中 scan 对象的 setCache 和 setBatch 方法的使用？"></a>请描述 HBase 中 scan 对象的 <code>setCache</code> 和 <code>setBatch</code> 方法的使用？</h2><ul><li><p><code>setCache</code> 用于设置缓存，即设置一次 RPC 请求可以获取多行数据。对于缓存操作，如果行的数据量非常大，多行数据有可能超过客户端进程的内存容量，由此引入批量处理这一解决方案。</p></li><li><p><code>setBatch</code> 用于设置批量处理，批量可以让用户选择每一次 <code>ResultScanner</code> 实例的 <code>next</code> 操作要取回多少列，例如，在扫描中设置 <code>setBatch(5)</code>，则一次 <code>next()</code> 返回的 Result 实例会包括 5 列。如果一行包括的列数超过了批量中设置的值，则可以将这一行分片，每次 <code>next</code> 操作返回一片，当一行的列数不能被批量中 设置的值整除时，最后一次返回的 Result 实例会包含比较少的列，如，一行 17 列，batch 设置为 5，则一共返回 4 个 Result 实例，这 4 个实例中包括的列数分别为 5、5、5、2。</p></li></ul><p>组合使用扫描器缓存和批量大小，可以让用户方便地控制扫描一个范围内的行键所需要的 RPC 调用次数。Cache 设置了服务器一次返回的行数， 而 Batch 设置了服务器一次返回的列数。</p><p>假如我们建立了一张有两个列族的表，添加了 10 行数据，每个行的每个列族下有 10 列，这意味着整个表一共有 200 列（或单元格，因为每个列只有一个版本），其中每行有 20 列。</p><div class="table-container"><table><thead><tr><th>缓存</th><th>批量处理</th><th>Result 个数</th><th>RPC 次数</th><th>说明</th></tr></thead><tbody><tr><td>1</td><td>1</td><td>200</td><td>201</td><td>每个列都作为一个 Result 实例返回， <br> 最后还多一个 RPC 确认扫描完成</td></tr><tr><td>200</td><td>1</td><td>200</td><td>2</td><td>每个 Result 实例都只包含一列的值， <br> 不过它们都被一次 RPC 请求取回（加一次完成检查）</td></tr><tr><td>2</td><td>10</td><td>20</td><td>11</td><td>批量参数是一行所包含的列数的一半， <br> 所以 200 列除以 10，需要 20 个 Result 实例。 <br> 同时需要 10 次 RPC 请求取回（加一次完成检查）</td></tr><tr><td>5</td><td>100</td><td>10</td><td>3</td><td>对于一行来讲，这个批量参数太大了， <br> 所以一行的 20 列都被放入了一个 Result 实例中。 <br> 同时缓存为 5，所以 10 个 Result 实例被两次 RPC 请求取回 <br> （加一次完成检查）</td></tr><tr><td>5</td><td>20</td><td>10</td><td>3</td><td>同上，不过这次的批量值与一行的列数正好相同， <br> 所以输出与上面一种情况相同</td></tr><tr><td>10</td><td>10</td><td>20</td><td>3</td><td>这次把表分成了较小的 Result 实例， <br> 但使用了较大的缓存值， <br> 所以也是只用了两次 RPC 请求就取回了数据</td></tr></tbody></table></div><ol><li><p>Batch 参数决定了一行数据分为几个 Result，它只针对一行数据，Batch 再大，也只能将一行的数据放入一个 Result 中。所以当一行数据有 10 列，而 Batch 为 100 时，也只能将一行的所有列都放入一个 Result，不会混合其他行；</p></li><li><p>缓存值决定一次 RPC 返回几个 Result，根据 Batch 划分的 Result 个数除以缓存个数可以得到 RPC 消息个数（之前定义缓存值决定一次返回的行数，这是不准确的，准确来说是决定一次 RPC 返回的 Result 个数，由于在引入 Batch 之前，一行封装为一个 Result，因此定义缓存值决定一次返回的行数，但引入 Batch 后，更准确的说法是缓存值决定了一次 RPC 返回的 Result 个数）；</p><p> RPC 请求次数 = (行数 * 每行列数) / Min(每行的列数，批量大小) / 扫描器缓存</p></li></ol><p>下图展示了缓存和批量两个参数如何联动，下图中有一个包含 9 行数据的表，每行都包含一些列。使用了一个缓存为 6，批量大小为 3 的扫描器，需要三次 RPC 请求来传送数据：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/HBase_rpc.png" alt="HBase RPC"></p><h2 id="请详细描述-HBase-中一个-cell-的结构？"><a href="#请详细描述-HBase-中一个-cell-的结构？" class="headerlink" title="请详细描述 HBase 中一个 cell 的结构？"></a>请详细描述 HBase 中一个 cell 的结构？</h2><p>HBase 中通过 row 和 columns 确定的为一个存贮单元称为 cell。</p><p>Cell：由 <code>&#123;row key, column(= + ), version&#125;</code> 唯一确定的单元。cell 中的数据是没有类型的，全部是字节码形式存储。</p><h2 id="简述-HBase-中-compact-用途是什么，什么时候触发，分为哪两种，有什么区别，有哪些相关配置参数？"><a href="#简述-HBase-中-compact-用途是什么，什么时候触发，分为哪两种，有什么区别，有哪些相关配置参数？" class="headerlink" title="简述 HBase 中 compact 用途是什么，什么时候触发，分为哪两种，有什么区别，有哪些相关配置参数？"></a>简述 HBase 中 compact 用途是什么，什么时候触发，分为哪两种，有什么区别，有哪些相关配置参数？</h2><p>在 HBase 中每当有 MemStore 数据 flush 到磁盘之后，就形成一个 StoreFile，当 StoreFile 的数量达到一定程度后，就需要将 StoreFile 文件来 进行 compaction 操作。</p><p>compact 的作用：</p><ul><li>合并文件</li><li>清除过期，多余版本的数据</li><li>提高读写数据的效率</li></ul><p>HBase 中实现了两种 compaction 的方式：Minor 和 Major，这两种 compaction 方式的区别是：</p><ul><li>Minor 操作只用来做部分文件的合并操作以及包括 <code>minVersion=0</code> 并且设置 ttl 的过期版本清理，不做任何删除数据、多版本数据的清理工作。</li><li>Major 操作是对 Region 下的 HStore 下的所有 StoreFile 执行合并操作，最终的结果是整理合并出一个文件。</li></ul><h2 id="每天百亿数据存入-HBase，如何保证数据的存储正确和在规定的时间里全部录入完毕，不残留数据？"><a href="#每天百亿数据存入-HBase，如何保证数据的存储正确和在规定的时间里全部录入完毕，不残留数据？" class="headerlink" title="每天百亿数据存入 HBase，如何保证数据的存储正确和在规定的时间里全部录入完毕，不残留数据？"></a>每天百亿数据存入 HBase，如何保证数据的存储正确和在规定的时间里全部录入完毕，不残留数据？</h2><p>需求分析：</p><ul><li>百亿数据：证明数据量非常大；</li><li>存入 HBase：证明是跟 HBase 的写入数据有关；</li><li>保证数据的正确：要设计正确的数据结构保证正确性；</li><li>在规定时间内完成：对存入速度是有要求的。</li></ul><p>解决思路：</p><ul><li>数据量百亿条，什么概念呢？假设一整天 60x60x24 = 86400 秒都在写入数据，那么每秒的写入条数高达 100 万条，HBase 当然是支持不了每秒百万条数据的，所以这百亿条数据可能不是通过实时地写入，而是批量地导入。批量导入推荐使用 BulkLoad 方式（推荐阅读：Spark 之读写 HBase），性能是普通写入方式几倍以上；</li><li>存入 HBase：普通写入是用 JavaAPI put 来实现，批量导入推荐使用 BulkLoad；</li><li>保证数据的正确：这里需要考虑 RowKey 的设计、预建分区和列族设计等问题；</li><li>在规定时间内完成也就是存入速度不能过慢，并且当然是越快越好，使用 BulkLoad。</li></ul><h2 id="请列举几个-HBase-优化方法？"><a href="#请列举几个-HBase-优化方法？" class="headerlink" title="请列举几个 HBase 优化方法？"></a>请列举几个 HBase 优化方法？</h2><ol><li><p>减少调整</p><p> 减少调整这个如何理解呢？HBase 中有几个内容会动态调整，如 region（分区）、HFile，所以通过一些方法来减少这些会带来 I/O 开销的调整。</p><ul><li>Region：如果没有预建分区的话，那么随着 region 中条数的增加，region 会进行分裂，这将增加 I/O 开销，所以解决方法就是根据你的 RowKey 设计来进行预建分区，减少region 的动态分裂。</li><li>HFile：HFile 是数据底层存储文件，在每个 MemStore 进行刷新时会生成一个 HFile，当 HFile 增加到一定程度时，会将属于一个 region 的 HFile 进行合并，这个步骤会带来开销但不可避免，但是合并后 HFile 大小如果大于设定的值，那么 HFile 会重新分裂。为了减少这样的无谓的 I/O 开销，建议估计项目数据量大小，给 HFile 设定一个合适的值。</li></ul></li><li><p>减少启停</p><p> 数据库事务机制就是为了更好地实现批量写入，较少数据库的开启关闭带来的开销，那么 HBase 中也存在频繁开启关闭带来的问题。</p><ul><li><p>关闭 Compaction，在闲时进行手动 Compaction。</p><p>因为 HBase 中存在 Minor Compaction 和 Major Compaction，也就是对 HFile 进行合并，所谓合并就是 I/O 读写，大量的 HFile 进行肯定会带来 I/O 开销，甚至是 I/O 风暴，所以为了避免这种不受控制的意外发生，建议关闭自动 Compaction，在闲时进行 Compaction。</p></li><li><p>批量数据写入时采用 BulkLoad。</p><p>如果通过 HBase-Shell 或者 JavaAPI 的 put 来实现大量数据的写入，那么性能差是肯定并且还可能带来一些意想不到的问题，所以当需要写入大量离线数据时建议使用 BulkLoad。</p></li></ul></li><li><p>减少数据量</p><p> 虽然我们是在进行大数据开发，但是如果可以通过某些方式在保证数据准确性同时减少数据量，何乐而不为呢？</p><ul><li><p>开启过滤，提高查询速度：开启 BloomFilter，BloomFilter 是列族级别的过滤，在生成一个 StoreFile 同时会生成一个 MetaBlock，用于查询时过滤数据。</p></li><li><p>使用压缩：一般推荐使用 Snappy 和 LZO 压缩。</p></li></ul></li><li><p>合理设计</p></li></ol><p>在一张 HBase 表格中 RowKey 和 ColumnFamily 的设计是非常重要，好的设计能够提高性能和保证数据的准确性。</p><ul><li><p>RowKey 设计：应该具备以下几个属性</p><ul><li>散列性：散列性能够保证相同相似的 RowKey 聚合，相异的 RowKey 分散，有利于查询。</li><li>简短性：RowKey 作为 key 的一部分存储在 HFile 中，如果为了可读性将 RowKey 设计得过长，那么将会增加存储压力。</li><li>唯一性：RowKey 必须具备明显的区别性。</li><li><p>业务性：举例来说：  假如我的查询条件比较多，而且不是针对列的条件，那么 RowKey 的设计就应该支持多条件查询。</p><p>  如果我的查询要求是最近插入的数据优先，那么 RowKey 则可以采用加上 <code>Long.Max-时间戳</code> 的方式，这样 RowKey 就是递减排列。</p></li></ul></li><li><p>列族的设计：列族的设计需要看应用场景</p><ul><li>优势：HBase 中数据时按列进行存储的，那么查询某一列族的某一列时就不需要全盘扫描，只需要扫描某一列族，减少了读 I/O；其实多列族设计对减少的作用不是很明显，适用于读多写少的场景</li><li>劣势：降低了写的 I/O 性能。原因如下：数据写到 store 以后是先缓存在 MemStore 中，同一个 region 中存在多个列族则存在多个 store，每个 store 都一个 MemStore，当其实 MemStore 进行 flush 时，属于同一个 region 的 store 中的 MemStore 都会进行 flush，增加 I/O 开销。</li></ul></li></ul><h2 id="Region-如何预建分区？"><a href="#Region-如何预建分区？" class="headerlink" title="Region 如何预建分区？"></a>Region 如何预建分区？</h2><p>预分区的目的主要是在创建表的时候指定分区数，提前规划表有多个分区，以及每个分区的区间范围，这样在存储的时候 RowKey 按照分区的区间存储，可以避免 region 热点问题。</p><p>通常有两种方案：</p><ul><li><p>shell 方法</p><p><code>create &#39;tb_splits&#39;, &#123;NAME =&gt; &#39;cf&#39;,VERSIONS=&gt; 3&#125;,&#123;SPLITS =&gt; [&#39;10&#39;,&#39;20&#39;,&#39;30&#39;]&#125;</code></p></li><li><p>JAVA 程序控制</p><ol><li>取样，先随机生成一定数量的 RowKey，将取样数据按升序排序放到一个集合里；</li><li>根据预分区的 region 个数，对整个集合平均分割，即是相关的 splitKeys；</li><li><code>HBaseAdmin.createTable(HTableDescriptor tableDescriptor, byte[] splitKeys)</code> 可以指定预分区的 splitKeys，即是指定 region 间的 RowKey 临界值。</li></ol></li></ul><h2 id="HRegionServer-宕机如何处理？"><a href="#HRegionServer-宕机如何处理？" class="headerlink" title="HRegionServer 宕机如何处理？"></a>HRegionServer 宕机如何处理？</h2><ol><li>Zookeeper 会监控 HRegionServer 的上下线情况，当 ZK 发现某个 HRegionServer 宕机之后会通知 HMaster 进行失效备援；</li><li>该 HRegionServer 会停止对外提供服务，就是它所负责的 region 暂时停止对外提供服务；</li><li>HMaster 会将该 HRegionServer 所负责的 region 转移到其他 HRegionServer 上，并且会对 HRegionServer 上存在 MemStore 中还未持久化到磁盘中的数据进行恢复；</li><li>这个恢复的工作是由 WAL 重播来完成，这个过程如下：<ol><li>wal 实际上就是一个文件，存在 <code>/HBase/WAL/</code> 对应 RegionServer 路径下。</li><li>宕机发生时，读取该 RegionServer 所对应的路径下的 wal 文件，然后根据不同的 region 切分成不同的临时文件 recover.edits。</li><li>当 region 被分配到新的 RegionServer 中，RegionServer 读取 region 时会进行是否存在 recover.edits，如果有则进行恢复。</li></ol></li></ol><h2 id="HBase-读写流程？"><a href="#HBase-读写流程？" class="headerlink" title="HBase 读写流程？"></a>HBase 读写流程？</h2><p><strong>读：</strong></p><ol><li>HRegionServer 保存着 Meta 表以及表数据，要访问表数据，首先 Client 先去访问 Zookeeper，从 Zookeeper 里面获取 Meta 表所在的位置信息，即找到这个 Meta 表在哪个 HRegionServer 上保存着。</li><li>接着 Client 通过刚才获取到的 HRegionServer 的 IP 来访问 Meta 表所在的 HRegionServer，从而读取到 Meta，进而获取到 Meta 表中存放的元数据。</li><li>Client 通过元数据中存储的信息，访问对应的 HRegionServer，然后扫描所在 HRegionServer 的 MemStore 和 StoreFile 来查询数据。</li><li>最后 HRegionServer 把查询到的数据响应给 Client。</li></ol><p><strong>写：</strong></p><ol><li>Client 先访问 Zookeeper，找到 Meta 表，并获取 Meta 表元数据。</li><li>确定当前将要写入的数据所对应的 HRegion 和 HRegionServer 服务器。</li><li>Client 向该 HRegionServer 服务器发起写入数据请求，然后 HRegionServer 收到请求并响应。</li><li>Client 先把数据写入到 HLog，以防止数据丢失。</li><li>然后将数据写入到 MemStore。</li><li>如果 HLog 和 MemStore 均写入成功，则这条数据写入成功。</li><li>如果 MemStore 达到阈值，会把 MemStore 中的数据 flush 到 StoreFile 中。</li><li>当 StoreFile 越来越多，会触发 Compact 合并操作，把过多的 StoreFile 合并成一个大的 StoreFile。</li><li>当 StoreFile 越来越大，Region 也会越来越大，达到阈值后，会触发 Split 操作，将 Region 一分为二。</li></ol><h2 id="HBase-内部机制是什么？"><a href="#HBase-内部机制是什么？" class="headerlink" title="HBase 内部机制是什么？"></a>HBase 内部机制是什么？</h2><p>HBase 是一个能适应联机业务的数据库系统。</p><ul><li>物理存储：HBase 的持久化数据是将数据存储在 HDFS 上。</li><li>存储管理：一个表是划分为很多 region 的，这些 region 分布式地存放在很多 RegionServer 上 Region 内部还可以划分为 store，store 内部有 MemStore 和 StoreFile。</li><li>版本管理：HBase 中的数据更新本质上是不断追加新的版本，通过 compact 操作来做版本间的文件合并 Region 的 split。</li><li>集群管理：Zookeeper + HMaster + HRegionServer。</li></ul><h2 id="HBase-中的-MemStore-是用来做什么的？"><a href="#HBase-中的-MemStore-是用来做什么的？" class="headerlink" title="HBase 中的 MemStore 是用来做什么的？"></a>HBase 中的 MemStore 是用来做什么的？</h2><p>HBase 为了保证随机读取的性能，所以 HFile 里面的 RowKey 是有序的。当客户端的请求在到达 RegionServer 之后，为了保证写入 RowKey 的有序性，所以不能将数据立刻写入到 HFile 中，而是将每个变更操作保存在内存中，也就是 MemStore 中。MemStore 能够很方便的支持操作的随机插入，并保证所有的操作在内存中是有序的。当 MemStore 达到一定的量之后，会将 MemStore 里面的数据 flush 到 HFile 中，这样能充分利用 hadoop 写入大文件的性能优势，提高写入性能。</p><p>由于 MemStore 是存放在内存中，如果 RegionServer 因为某种原因死了，会导致内存中数据丢失。所有为了保证数据不丢失，HBase 将更新操作在写入 MemStore 之前会写入到一个 write ahead log(WAL) 中。WAL 文件是追加、顺序写入的，WAL 每个 RegionServer 只有一个，同一个 RegionServer 上所有 region 写入同一个的 WAL 文件。这样当某个 RegionServer 失败时，可以通过 WAL 文件，将所有的操作顺序重新加载到 MemStore 中。</p><h2 id="HBase-在进行模型设计时重点在什么地方？一张表中定义多少个-Column-Family-最合适？为什么？"><a href="#HBase-在进行模型设计时重点在什么地方？一张表中定义多少个-Column-Family-最合适？为什么？" class="headerlink" title="HBase 在进行模型设计时重点在什么地方？一张表中定义多少个 Column Family 最合适？为什么？"></a>HBase 在进行模型设计时重点在什么地方？一张表中定义多少个 Column Family 最合适？为什么？</h2><p>Column Family 的个数具体看表的数据，一般来说划分标准是根据数据访问频度，如一张表里有些列访问相对频繁，而另一些列访问很少，这时可以把这张表划分成两个列族，分开存储，提高访问效率。</p><h2 id="如何提高-HBase-客户端的读写性能？请举例说明。"><a href="#如何提高-HBase-客户端的读写性能？请举例说明。" class="headerlink" title="如何提高 HBase 客户端的读写性能？请举例说明。"></a>如何提高 HBase 客户端的读写性能？请举例说明。</h2><ol><li>开启 bloomfilter 过滤器，开启 bloomfilter 比没开启要快 3、4 倍；</li><li>HBase 对于内存有特别的需求，在硬件允许的情况下配足够多的内存给它；</li><li>通过修改 <code>hbase-env.sh</code> 中的 <code>export HBASE_HEAPSIZE=3000</code>，这里默认为 1000m；</li><li>增大 RPC 数量，通过修改 <code>hbase-site.xml</code> 中的 <code>hbase.regionserver.handler.count</code>属性，可以适当的放大 RPC 数量，默认值为 10 有点小。</li></ol><h2 id="HBase-集群安装注意事项？"><a href="#HBase-集群安装注意事项？" class="headerlink" title="HBase 集群安装注意事项？"></a>HBase 集群安装注意事项？</h2><ol><li>HBase 需要 HDFS 的支持，因此安装 HBase 前确保 Hadoop 集群安装完成；</li><li>HBase 需要 Zookeeper 集群的支持，因此安装 HBase 前确保 Zookeeper 集群安装完成；</li><li>注意 HBase 与 Hadoop 的版本兼容性；</li><li>注意 <code>hbase-env.sh</code> 配置文件和 <code>hbase-site.xml</code> 配置文件的正确配置；</li><li>注意 RegionServers 配置文件的修改；</li><li>注意集群中的各个节点的时间必须同步，否则启动 HBase 集群将会报错。</li></ol><h2 id="直接将时间戳作为行健，在写入单个-region-时候会发生热点问题，为什么呢？"><a href="#直接将时间戳作为行健，在写入单个-region-时候会发生热点问题，为什么呢？" class="headerlink" title="直接将时间戳作为行健，在写入单个 region 时候会发生热点问题，为什么呢？"></a>直接将时间戳作为行健，在写入单个 region 时候会发生热点问题，为什么呢？</h2><p>region 中的 RowKey 是有序存储，若时间比较集中。就会存储到一个 region 中，这样一个 region 的数据变多，其它的 region 数据很少，加载数据就会很慢，直到 region 分裂，此问题才会得到缓解。</p><h2 id="请描述如何解决-HBase-中-region-太小和-region-太大带来的冲突？"><a href="#请描述如何解决-HBase-中-region-太小和-region-太大带来的冲突？" class="headerlink" title="请描述如何解决 HBase 中 region 太小和 region 太大带来的冲突？"></a>请描述如何解决 HBase 中 region 太小和 region 太大带来的冲突？</h2><p>Region 过大会发生多次 compaction，将数据读一遍并重写一遍到 HDFS 上，占用 I/O，region 过小会造成多次 split，region 会下线，影响访问服务，最佳的解决方法是调整 <code>hbase.hregion.max.filesize</code> 为 256m。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/HBase/">HBase</category>
      
      
      <comments>https://blog.eurkon.com/post/47457d03.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Hexo 博客访问统计图（弃用）</title>
      <link>https://blog.eurkon.com/post/ef1da941.html</link>
      <guid>https://blog.eurkon.com/post/ef1da941.html</guid>
      <pubDate>Thu, 01 Apr 2021 04:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;本教程已经弃用，请查看新教程 &lt;a href=&quot;/post/61763977.html&quot;&gt;Hexo 博客实时访问统</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>本教程已经弃用，请查看新教程 <a href="/post/61763977.html">Hexo 博客实时访问统计图</a></p></blockquote><p>本文教程主要针对 Hexo 博客，对博客站点的的访问地图、每月访问量、访问来源的维度绘制统计图，使用的是 <a href="https://echarts.apache.org/examples/zh/index.html">ECharts</a> 开源可视化库。具体效果可以点击本站的 <a href="/census/">统计--博客统计</a> 页面查看。</p><blockquote><ul><li>本文数据来源均为百度统计，请确保博客站点已加入百度统计，以 butterfly 主题为例，可参照 <a href="https://butterfly.js.org/posts/ceeb73f/#%E5%88%86%E6%9E%90%E7%B5%B1%E8%A8%88">Butterfly 安装文档(四) 主题配置-2</a> 的分析统计段落实现。</li><li>本地主机访问（localhost）也会记录到百度统计，推荐在 <strong>【百度统计】--【管理】--【统计规则设置】--【过滤规则设置】--【受访域名统计规则】--【勾选排除 localhost（本地主机）】</strong> 排除本地主机访问（貌似在勾选后生效，但是以前的访问记录仍会统计）。</li><li>绘制统计图的数据在生成后不会变化。</li><li><code>2021-05-14</code> 新增访客数的统计，统计指标 <code>metrics</code> 可以选择统计访问次数（PV）还是访客数（UV）。</li><li><code>2021-05-17</code> 新增主题“明暗模式”下统计图的颜色切换。</li><li><code>2021-05-17</code> 针对访问数据的实时性问题，发表了新教程 <a href="/post/61763977.html">Hexo 博客实时访问统计图</a>。</li></ul></blockquote><p>如果想绘制博客文章发布统计图的可以参考文章 <a href="/post/1213ef82.html">Hexo 博客文章统计图</a></p><h2 id="新建-census-页面"><a href="#新建-census-页面" class="headerlink" title="新建 census 页面"></a>新建 census 页面</h2><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab active">使用命令创建</button><button type="button" class="tab">手动创建</button></div><div class="tab-contents"><div class="tab-item-content active"><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo new page census</span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content"><p>在 <code>[Blogroot]\source\</code> 目录下新建 <code>census</code> 文件夹，并在新建的 <code>census</code> 文件夹下新建 <code>index.md</code> 文件，添加以下内容：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: 博客统计</span><br><span class="line">date: 2020-03-01 08:00:00</span><br><span class="line">---</span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><h2 id="引入-ECharts-js"><a href="#引入-ECharts-js" class="headerlink" title="引入 ECharts.js"></a>引入 ECharts.js</h2><blockquote><p>echarts.js 必须在渲染 echarts 实例的 JavaScript 前引入。</p></blockquote><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab active">全局引入</button><button type="button" class="tab">页面引入</button></div><div class="tab-contents"><div class="tab-item-content active"><p>以 butterfly 主题为例，可以在 <code>[Blogroot]\_config.butterfly.yml</code> 的 <code>inject</code> 配置项中引入 <code>echart.js</code> 文件。</p><p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line">  <span class="attr">head:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&lt;script</span> <span class="string">src=&quot;https://npm.elemecdn.com/echarts@4.9.0/dist/echarts.min.js&quot;&gt;&lt;/script&gt;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&lt;script</span> <span class="string">src=&quot;https://npm.elemecdn.com/echarts@4.9.0/map/js/china.js&quot;&gt;&lt;/script&gt;</span> <span class="comment">## 绘制地图需要另外添加 china.js</span></span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content"><p>可以在 <code>index.md</code> 添加以下内容：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;https://npm.elemecdn.com/echarts@4.9.0/dist/echarts.min.js&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;https://npm.elemecdn.com/echarts@4.9.0/map/js/china.js&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span> &lt;!-- 绘制地图需要另外添加 china.js --&gt;</span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><h2 id="博客访问统计"><a href="#博客访问统计" class="headerlink" title="博客访问统计"></a>博客访问统计</h2><h3 id="获取网站统计数据"><a href="#获取网站统计数据" class="headerlink" title="获取网站统计数据"></a>获取网站统计数据</h3><ol><li><p>已经添加百度统计分析的小伙伴可以登录<a href="https://tongji.baidu.com/web/welcome/login">百度统计</a>的官网网站，此处博主使用的是百度账号登录。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/login.png" alt="百度账号登录"></p></li><li><p>登录进入首页后可以点击<strong>基础报告</strong>查看网站访问统计数据（若无绑定网站，需要先在 <strong>管理</strong>页面--<strong>账户管理</strong>--<strong>网站列表</strong> 添加博客网址）。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/index.png" alt="首页"></p></li><li><p>此时我们想要获取这些数据可以调用<strong>百度统计 API</strong>，点击<strong>管理</strong>，进入管理页面后在左边侧边栏找到<strong>数据导出服务</strong>。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/data_export.png" alt="数据导出服务"></p></li><li><p>登录<a href="http://developer.baidu.com/console#app/project">百度开发者中心控制台</a>，创建工程，应用名称任意。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/project.png" alt="创建工程"></p></li><li><p>点击刚刚创建的工程，记录下 API Key，Secret Key。</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/project_info.png" alt="基本信息"></p></li><li><p>填写下面链接参数后打开链接获取授权码，具体步骤可以参考 <a href="https://tongji.baidu.com/api/manual/">Tongji API 用户手册</a>。</p><p> <code>http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&amp;client_id=&#123;CLIENT_ID&#125;&amp;redirect_uri=&#123;REDIRECT_URI&#125;&amp;scope=basic&amp;display=popup</code></p><ul><li>API Key：{CLIENT_ID}</li><li><p>回调 URI：{REDIRECT_URI}，可以填写 <code>oob</code>。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/auth_code.png" alt="授权码"></p></li></ul></li><li><p>复制第 6 步获取的授权码，填写下面链接参数后打开链接获取 <code>ACCESS_TOKEN</code> ：。</p><p> <code>http://openapi.baidu.com/oauth/2.0/token?grant_type=authorization_code&amp;code=&#123;CODE&#125;&amp;client_id=&#123;CLIENT_ID&#125;&amp;client_secret=&#123;CLIENT_SECRET&#125;&amp;redirect_uri=&#123;REDIRECT_URI&#125;</code></p><ul><li>Auth Code：{CODE}，第 6 步获取的授权码。</li><li>API Key：{CLIENT_ID}</li><li>Secret Key：{CLIENT_SECRET}</li><li><p>回调 URI：{REDIRECT_URI}，可以填写 <code>oob</code>。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/access_token.png" alt="ACCESS_TOKEN"></p></li></ul><p><strong>注意：</strong>从上述步骤得到的数据中包含 <code>ACCESS_TOKEN</code> 和 <code>REFRESH_TOKEN</code> 两个值，其中 <code>ACCESS_TOKEN</code> 的有效期为一个月，<code>REFRESH_TOKEN</code> 的有效期为十年。<code>REFRESH_TOKEN</code> 的作用就是刷新获取新的 <code>ACCESS_TOKEN</code> 和 <code>REFRESH_TOKEN</code> ， 如此反复操作来实现 <code>ACCESS_TOKEN</code> 有效期永久的机制。</p><p>一旦 <code>ACCESS_TOKEN</code> 过期，可根据以下请求更换新的 <code>ACCESS_TOKEN</code> 和 <code>REFRESH_TOKEN</code>：</p><p><code>http://openapi.baidu.com/oauth/2.0/token?grant_type=refresh_token&amp;refresh_token=&#123;REFRESH_TOKEN&#125;&amp;client_id=&#123;CLIENT_ID&#125;&amp;client_secret=&#123;CLIENT_SECRET&#125;</code></p><ul><li>API Key：{CLIENT_ID}</li><li>Secret Key：{CLIENT_SECRET}</li><li>Refresh Token：{REFRESH_TOKEN}</li></ul></li><li><p>调用百度统计 API</p><p> 第 7 步获取的 <code>ACCESS_TOKEN</code> 是所调用 API 的用户级参数，结合各 API 的应用级参数即可正常调用 API 获取数据，填写下面链接参数后打开链接获取网站 ID：</p><p> <code>https://openapi.baidu.com/rest/2.0/tongji/config/getSiteList?access_token=&#123;ACCESS_TOKEN&#125;</code></p><ul><li><p>Access Token：{ACCESS_TOKEN}</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/site_id.png" alt="网址 ID"></p></li></ul><p>也可以在 <a href="https://tongji.baidu.com/api/debug/#">Tongji API 调试工具</a> 输入 <code>ACCESS_TOKEN</code> 获取网址 ID：</p><p> <img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/site_id_debug.png" alt="API 调试工具"></p></li><li><p>在 <a href="https://tongji.baidu.com/api/debug/#">Tongji API 调试工具</a> 选择需要获取的报告数据，填写 <code>ACCESS_TOKEN</code> 、必填参数、选填参数获取百度统计数据。</p></li></ol><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_census/site_data.png" alt="获取百度统计数据"></p><h3 id="网站统计代码"><a href="#网站统计代码" class="headerlink" title="网站统计代码"></a>网站统计代码</h3><p>以 butterfly 主题为例，可以在 <code>[Blogroot]\themes\butterfly\scripts\helpers\</code> 目录下新建 <code>census.js</code> 文件，然后添加以下内容：</p><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> cheerio = <span class="built_in">require</span>(<span class="string">&#x27;cheerio&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> moment = <span class="built_in">require</span>(<span class="string">&#x27;moment&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> fetch = <span class="built_in">require</span>(<span class="string">&#x27;node-fetch&#x27;</span>)</span><br><span class="line"></span><br><span class="line">hexo.extend.filter.register(<span class="string">&#x27;after_render:html&#x27;</span>, <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> (<span class="params">locals</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> $ = cheerio.load(locals)</span><br><span class="line">  <span class="keyword">const</span> map = $(<span class="string">&#x27;#map-chart&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> trend = $(<span class="string">&#x27;#trends-chart&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> source = $(<span class="string">&#x27;#sources-chart&#x27;</span>)</span><br><span class="line">  <span class="keyword">let</span> htmlEncode = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (map.length &gt; <span class="number">0</span> || trend.length &gt; <span class="number">0</span> || source.length &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (map.length &gt; <span class="number">0</span> &amp;&amp; $(<span class="string">&#x27;#mapChart&#x27;</span>).length === <span class="number">0</span>) &#123;</span><br><span class="line">      map.after(<span class="keyword">await</span> mapChart())</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (trend.length &gt; <span class="number">0</span> &amp;&amp; $(<span class="string">&#x27;#trendsChart&#x27;</span>).length === <span class="number">0</span>) &#123;</span><br><span class="line">      trend.after(<span class="keyword">await</span> trendsChart())</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (source.length &gt; <span class="number">0</span> &amp;&amp; $(<span class="string">&#x27;#sourcesChart&#x27;</span>).length === <span class="number">0</span>) &#123;</span><br><span class="line">      source.after(<span class="keyword">await</span> sourcesChart())</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (htmlEncode) &#123;</span><br><span class="line">      <span class="keyword">return</span> $.root().html().replace(<span class="regexp">/&amp;amp;#/g</span>, <span class="string">&#x27;&amp;#&#x27;</span>)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> $.root().html()</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> locals</span><br><span class="line">  &#125;</span><br><span class="line">&#125;, <span class="number">15</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> startDate = <span class="string">&#x27;20210101&#x27;</span> <span class="comment">// 开始日期</span></span><br><span class="line"><span class="keyword">const</span> endDate = moment().format(<span class="string">&#x27;YYYYMMDD&#x27;</span>) <span class="comment">// 结束日期</span></span><br><span class="line"><span class="keyword">const</span> accessToken = <span class="string">&#x27;121.c644d8c4*****&#x27;</span> <span class="comment">// accessToken</span></span><br><span class="line"><span class="keyword">const</span> siteId = <span class="string">&#x27;16****&#x27;</span> <span class="comment">// 网址 id</span></span><br><span class="line"><span class="keyword">const</span> dataUrl = <span class="string">&#x27;https://openapi.baidu.com/rest/2.0/tongji/report/getData?access_token=&#x27;</span> + accessToken + <span class="string">&#x27;&amp;site_id=&#x27;</span> + siteId;</span><br><span class="line"><span class="keyword">const</span> metrics = <span class="string">&#x27;pv_count&#x27;</span> <span class="comment">// 统计访问次数 PV 填写 &#x27;pv_count&#x27;，统计访客数 UV 填写 &#x27;visitor_count&#x27;，二选一</span></span><br><span class="line"><span class="keyword">const</span> metricsName = (metrics === <span class="string">&#x27;pv_count&#x27;</span> ? <span class="string">&#x27;访问次数&#x27;</span> : (metrics === <span class="string">&#x27;visitor_count&#x27;</span> ? <span class="string">&#x27;访客数&#x27;</span> : <span class="string">&#x27;&#x27;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment">// 访问地图</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mapChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// const paramUrl = &#x27;&amp;start_date=&#x27; + startDate + &#x27;&amp;end_date=&#x27; + endDate + &#x27;&amp;metrics=&#x27; + metrics + &#x27;&amp;method=visit/district/a&#x27;; // 更换请求地址</span></span><br><span class="line">    <span class="keyword">const</span> paramUrl = <span class="string">&#x27;&amp;start_date=&#x27;</span> + startDate + <span class="string">&#x27;&amp;end_date=&#x27;</span> + endDate + <span class="string">&#x27;&amp;metrics=&#x27;</span> + metrics + <span class="string">&#x27;&amp;method=overview/getDistrictRpt&#x27;</span>;</span><br><span class="line">    fetch(dataUrl + paramUrl).then(<span class="function"><span class="params">data</span> =&gt;</span> data.json()).then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> monthArr = [];</span><br><span class="line">      <span class="keyword">let</span> mapName = data.result.items[<span class="number">0</span>]</span><br><span class="line">      <span class="keyword">let</span> mapValue = data.result.items[<span class="number">1</span>]</span><br><span class="line">      <span class="keyword">let</span> mapArr = []</span><br><span class="line">      <span class="keyword">let</span> max = mapValue[<span class="number">0</span>][<span class="number">0</span>]</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; mapName.length; i++) &#123;</span><br><span class="line">        <span class="comment">// mapArr.push(&#123; name: mapName[i][0].name, value: mapValue[i][0] &#125;)</span></span><br><span class="line">        mapArr.push(&#123; <span class="attr">name</span>: mapName[i][<span class="number">0</span>], <span class="attr">value</span>: mapValue[i][<span class="number">0</span>] &#125;)</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">const</span> mapArrJson = <span class="built_in">JSON</span>.stringify(mapArr)</span><br><span class="line">      resolve(<span class="string">`</span></span><br><span class="line"><span class="string">        &lt;script id=&quot;mapChart&quot;&gt;</span></span><br><span class="line"><span class="string">          var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line"><span class="string">          var mapChart = echarts.init(document.getElementById(&#x27;map-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">          var mapOption = &#123;</span></span><br><span class="line"><span class="string">            title: &#123;</span></span><br><span class="line"><span class="string">              text: &#x27;博客访问来源地图&#x27;,</span></span><br><span class="line"><span class="string">              x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">              textStyle: &#123;</span></span><br><span class="line"><span class="string">                color: color</span></span><br><span class="line"><span class="string">              &#125;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            tooltip: &#123;</span></span><br><span class="line"><span class="string">              trigger: &#x27;item&#x27;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            visualMap: &#123;</span></span><br><span class="line"><span class="string">              min: 0,</span></span><br><span class="line"><span class="string">              max: <span class="subst">$&#123;max&#125;</span>,</span></span><br><span class="line"><span class="string">              left: &#x27;left&#x27;,</span></span><br><span class="line"><span class="string">              top: &#x27;bottom&#x27;,</span></span><br><span class="line"><span class="string">              text: [&#x27;高&#x27;,&#x27;低&#x27;],</span></span><br><span class="line"><span class="string">              color: [&#x27;#37a2da&#x27;, &#x27;#92d0f9&#x27;],</span></span><br><span class="line"><span class="string">              textStyle: &#123;</span></span><br><span class="line"><span class="string">                color: color</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              calculable: true</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            series: [&#123;</span></span><br><span class="line"><span class="string">              name: &#x27;<span class="subst">$&#123;metricsName&#125;</span>&#x27;,</span></span><br><span class="line"><span class="string">              type: &#x27;map&#x27;,</span></span><br><span class="line"><span class="string">              mapType: &#x27;china&#x27;,</span></span><br><span class="line"><span class="string">              showLegendSymbol: false,</span></span><br><span class="line"><span class="string">              label: &#123;</span></span><br><span class="line"><span class="string">                normal: &#123;</span></span><br><span class="line"><span class="string">                  show: false</span></span><br><span class="line"><span class="string">                &#125;,</span></span><br><span class="line"><span class="string">                emphasis: &#123;</span></span><br><span class="line"><span class="string">                  show: true,</span></span><br><span class="line"><span class="string">                  color: &#x27;#617282&#x27;</span></span><br><span class="line"><span class="string">                &#125;</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              itemStyle: &#123;</span></span><br><span class="line"><span class="string">                normal: &#123;</span></span><br><span class="line"><span class="string">                  areaColor: &#x27;rgb(230, 232, 234)&#x27;,</span></span><br><span class="line"><span class="string">                  borderColor: &#x27;rgb(255, 255, 255)&#x27;,</span></span><br><span class="line"><span class="string">                  borderWidth: 1</span></span><br><span class="line"><span class="string">                &#125;,</span></span><br><span class="line"><span class="string">                emphasis: &#123;</span></span><br><span class="line"><span class="string">                  areaColor: &#x27;gold&#x27;</span></span><br><span class="line"><span class="string">                &#125;</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              data: <span class="subst">$&#123;mapArrJson&#125;</span></span></span><br><span class="line"><span class="string">            &#125;]</span></span><br><span class="line"><span class="string">          &#125;;</span></span><br><span class="line"><span class="string">          mapChart.setOption(mapOption);</span></span><br><span class="line"><span class="string">          window.addEventListener(&quot;resize&quot;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">            mapChart.resize();</span></span><br><span class="line"><span class="string">          &#125;);</span></span><br><span class="line"><span class="string">        &lt;/script&gt;`</span>);</span><br><span class="line">    &#125;).catch(<span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 访问趋势</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">trendsChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> paramUrl = <span class="string">&#x27;&amp;start_date=&#x27;</span> + startDate + <span class="string">&#x27;&amp;end_date=&#x27;</span> + endDate + <span class="string">&#x27;&amp;metrics=&#x27;</span> + metrics + <span class="string">&#x27;&amp;method=trend/time/a&amp;gran=month&#x27;</span></span><br><span class="line">    fetch(dataUrl + paramUrl).then(<span class="function"><span class="params">data</span> =&gt;</span> data.json()).then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> monthValueArr = []</span><br><span class="line">      <span class="keyword">const</span> monthName = data.result.items[<span class="number">0</span>]</span><br><span class="line">      <span class="keyword">const</span> monthValue = data.result.items[<span class="number">1</span>]</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> i = monthName.length - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">        monthArr.push(monthName[i][<span class="number">0</span>].substring(<span class="number">0</span>, <span class="number">7</span>).replace(<span class="string">&#x27;/&#x27;</span>, <span class="string">&#x27;-&#x27;</span>))</span><br><span class="line">        monthValueArr.push(monthValue[i][<span class="number">0</span>] !== <span class="string">&#x27;--&#x27;</span> ? monthValue[i][<span class="number">0</span>] : <span class="number">0</span>)</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">const</span> monthArrJson = <span class="built_in">JSON</span>.stringify(monthArr)</span><br><span class="line">      <span class="keyword">const</span> monthValueArrJson = <span class="built_in">JSON</span>.stringify(monthValueArr)</span><br><span class="line">      resolve(<span class="string">`</span></span><br><span class="line"><span class="string">        &lt;script id=&quot;trendsChart&quot;&gt;</span></span><br><span class="line"><span class="string">          var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line"><span class="string">          var trendsChart = echarts.init(document.getElementById(&#x27;trends-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">          var trendsOption = &#123;</span></span><br><span class="line"><span class="string">            title: &#123;</span></span><br><span class="line"><span class="string">              text: &#x27;博客访问统计图&#x27;,</span></span><br><span class="line"><span class="string">              x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">              textStyle: &#123;</span></span><br><span class="line"><span class="string">                color: color</span></span><br><span class="line"><span class="string">              &#125;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            tooltip: &#123;</span></span><br><span class="line"><span class="string">              trigger: &#x27;axis&#x27;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            xAxis: &#123;</span></span><br><span class="line"><span class="string">              name: &#x27;日期&#x27;,</span></span><br><span class="line"><span class="string">              type: &#x27;category&#x27;,</span></span><br><span class="line"><span class="string">              boundaryGap: false,</span></span><br><span class="line"><span class="string">              nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">                color: color</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              axisTick: &#123;</span></span><br><span class="line"><span class="string">                show: false</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              axisLabel: &#123;</span></span><br><span class="line"><span class="string">                show: true,</span></span><br><span class="line"><span class="string">                color: color</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              axisLine: &#123;</span></span><br><span class="line"><span class="string">                show: true,</span></span><br><span class="line"><span class="string">                lineStyle: &#123;</span></span><br><span class="line"><span class="string">                  color: color</span></span><br><span class="line"><span class="string">                &#125;</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              data: <span class="subst">$&#123;monthArrJson&#125;</span></span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            yAxis: &#123;</span></span><br><span class="line"><span class="string">              name: &#x27;<span class="subst">$&#123;metricsName&#125;</span>&#x27;,</span></span><br><span class="line"><span class="string">              type: &#x27;value&#x27;,</span></span><br><span class="line"><span class="string">              nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">                color: color</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              splitLine: &#123;</span></span><br><span class="line"><span class="string">                show: false</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              axisTick: &#123;</span></span><br><span class="line"><span class="string">                show: false</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              axisLabel: &#123;</span></span><br><span class="line"><span class="string">                show: true,</span></span><br><span class="line"><span class="string">                color: color</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              axisLine: &#123;</span></span><br><span class="line"><span class="string">                show: true,</span></span><br><span class="line"><span class="string">                lineStyle: &#123;</span></span><br><span class="line"><span class="string">                  color: color</span></span><br><span class="line"><span class="string">                &#125;</span></span><br><span class="line"><span class="string">              &#125;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            series: [&#123;</span></span><br><span class="line"><span class="string">              name: &#x27;<span class="subst">$&#123;metricsName&#125;</span>&#x27;,</span></span><br><span class="line"><span class="string">              type: &#x27;line&#x27;,</span></span><br><span class="line"><span class="string">              smooth: true,</span></span><br><span class="line"><span class="string">              lineStyle: &#123;</span></span><br><span class="line"><span class="string">                  width: 0</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              showSymbol: false,</span></span><br><span class="line"><span class="string">              itemStyle: &#123;</span></span><br><span class="line"><span class="string">                opacity: 1,</span></span><br><span class="line"><span class="string">                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">                  offset: 0,</span></span><br><span class="line"><span class="string">                  color: &#x27;rgba(128, 255, 165)&#x27;</span></span><br><span class="line"><span class="string">                &#125;,</span></span><br><span class="line"><span class="string">                &#123;</span></span><br><span class="line"><span class="string">                  offset: 1,</span></span><br><span class="line"><span class="string">                  color: &#x27;rgba(1, 191, 236)&#x27;</span></span><br><span class="line"><span class="string">                &#125;])</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              areaStyle: &#123;</span></span><br><span class="line"><span class="string">                opacity: 1,</span></span><br><span class="line"><span class="string">                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">                  offset: 0,</span></span><br><span class="line"><span class="string">                  color: &#x27;rgba(128, 255, 165)&#x27;</span></span><br><span class="line"><span class="string">                &#125;, &#123;</span></span><br><span class="line"><span class="string">                  offset: 1,</span></span><br><span class="line"><span class="string">                  color: &#x27;rgba(1, 191, 236)&#x27;</span></span><br><span class="line"><span class="string">                &#125;])</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              data: <span class="subst">$&#123;monthValueArrJson&#125;</span>,</span></span><br><span class="line"><span class="string">              markLine: &#123;</span></span><br><span class="line"><span class="string">                data: [&#123;</span></span><br><span class="line"><span class="string">                  name: &#x27;平均值&#x27;,</span></span><br><span class="line"><span class="string">                  type: &#x27;average&#x27;,</span></span><br><span class="line"><span class="string">                  label: &#123;</span></span><br><span class="line"><span class="string">                    color: color</span></span><br><span class="line"><span class="string">                  &#125;</span></span><br><span class="line"><span class="string">                &#125;]</span></span><br><span class="line"><span class="string">              &#125;</span></span><br><span class="line"><span class="string">            &#125;]</span></span><br><span class="line"><span class="string">          &#125;;</span></span><br><span class="line"><span class="string">          trendsChart.setOption(trendsOption);</span></span><br><span class="line"><span class="string">          window.addEventListener(&quot;resize&quot;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">            trendsChart.resize();</span></span><br><span class="line"><span class="string">          &#125;);</span></span><br><span class="line"><span class="string">        &lt;/script&gt;`</span>)</span><br><span class="line">    &#125;).catch(<span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 访问来源</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sourcesChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> paramUrl = <span class="string">&#x27;&amp;start_date=&#x27;</span> + startDate + <span class="string">&#x27;&amp;end_date=&#x27;</span> + endDate + <span class="string">&#x27;&amp;metrics=&#x27;</span> + metrics + <span class="string">&#x27;&amp;method=source/all/a&#x27;</span>;</span><br><span class="line">    fetch(dataUrl + paramUrl).then(<span class="function"><span class="params">data</span> =&gt;</span> data.json()).then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">      monthArr = [];</span><br><span class="line">      <span class="keyword">let</span> sourcesName = data.result.items[<span class="number">0</span>]</span><br><span class="line">      <span class="keyword">let</span> sourcesValue = data.result.items[<span class="number">1</span>]</span><br><span class="line">      <span class="keyword">let</span> sourcesArr = []</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; sourcesName.length; i++) &#123;</span><br><span class="line">        sourcesArr.push(&#123; <span class="attr">name</span>: sourcesName[i][<span class="number">0</span>].name, <span class="attr">value</span>: sourcesValue[i][<span class="number">0</span>] &#125;)</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">const</span> sourcesArrJson = <span class="built_in">JSON</span>.stringify(sourcesArr)</span><br><span class="line">      resolve(<span class="string">`</span></span><br><span class="line"><span class="string">        &lt;script id=&quot;sourcesChart&quot;&gt;</span></span><br><span class="line"><span class="string">          var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line"><span class="string">          var sourcesChart = echarts.init(document.getElementById(&#x27;sources-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">          var sourcesOption = &#123;</span></span><br><span class="line"><span class="string">            title: &#123;</span></span><br><span class="line"><span class="string">              text: &#x27;博客访问来源统计图&#x27;,</span></span><br><span class="line"><span class="string">              x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">              textStyle: &#123;</span></span><br><span class="line"><span class="string">                color: color</span></span><br><span class="line"><span class="string">              &#125;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            legend: &#123;</span></span><br><span class="line"><span class="string">              top: &#x27;bottom&#x27;,</span></span><br><span class="line"><span class="string">              textStyle: &#123;</span></span><br><span class="line"><span class="string">                color: color</span></span><br><span class="line"><span class="string">              &#125;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            tooltip: &#123;</span></span><br><span class="line"><span class="string">              trigger: &#x27;item&#x27;,</span></span><br><span class="line"><span class="string">              formatter: &quot;&#123;a&#125; &lt;br/&gt;&#123;b&#125; : &#123;c&#125; (&#123;d&#125;%)&quot;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            series: [&#123;</span></span><br><span class="line"><span class="string">              name: &#x27;<span class="subst">$&#123;metricsName&#125;</span>&#x27;,</span></span><br><span class="line"><span class="string">              type: &#x27;pie&#x27;,</span></span><br><span class="line"><span class="string">              radius: [30, 80],</span></span><br><span class="line"><span class="string">              center: [&#x27;50%&#x27;, &#x27;50%&#x27;],</span></span><br><span class="line"><span class="string">              roseType: &#x27;area&#x27;,</span></span><br><span class="line"><span class="string">              label: &#123;</span></span><br><span class="line"><span class="string">                color: color,</span></span><br><span class="line"><span class="string">                formatter: &quot;&#123;b&#125; : &#123;c&#125; (&#123;d&#125;%)&quot;</span></span><br><span class="line"><span class="string">              &#125;,</span></span><br><span class="line"><span class="string">              data: <span class="subst">$&#123;sourcesArrJson&#125;</span>,</span></span><br><span class="line"><span class="string">              itemStyle: &#123;</span></span><br><span class="line"><span class="string">                emphasis: &#123;</span></span><br><span class="line"><span class="string">                  shadowBlur: 10,</span></span><br><span class="line"><span class="string">                  shadowOffsetX: 0,</span></span><br><span class="line"><span class="string">                  shadowColor: &#x27;rgba(255, 255, 255, 0.5)&#x27;</span></span><br><span class="line"><span class="string">                &#125;</span></span><br><span class="line"><span class="string">              &#125;</span></span><br><span class="line"><span class="string">            &#125;]</span></span><br><span class="line"><span class="string">          &#125;;</span></span><br><span class="line"><span class="string">          sourcesChart.setOption(sourcesOption);</span></span><br><span class="line"><span class="string">          window.addEventListener(&quot;resize&quot;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">            sourcesChart.resize();</span></span><br><span class="line"><span class="string">          &#125;);</span></span><br><span class="line"><span class="string">        &lt;/script&gt;`</span>);</span><br><span class="line">    &#125;).catch(<span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>上面是博主自己三个统计图的代码，小伙伴们可以用以参考，根据自己的需求绘制百度分析统计图。</p><p>更多统计图的自定义属性可以查看 <a href="https://echarts.apache.org/zh/option.html">ECharts 配置项文档</a>，根据自行喜好对 ECharts 统计图进行修改。</p><h2 id="使用统计图"><a href="#使用统计图" class="headerlink" title="使用统计图"></a>使用统计图</h2><p>在上文新建的 <code>[Blogroot]\source\census\index.md</code> 文件中添加以下内容：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 访问地图 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;map-chart&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 600px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&lt;!-- 访问趋势 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;trends-chart&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 300px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&lt;!-- 访问来源 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;sources-chart&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 300px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br></pre></td></tr></table></figure></p><p>当然也可以在其他页面引入博客访问统计图。</p><h2 id="适配明暗模式"><a href="#适配明暗模式" class="headerlink" title="适配明暗模式"></a>适配明暗模式</h2><p>统计图内部文字的颜色通过 <code>census.js</code> 文件中的 <code>var color = &#39;#000&#39;</code> 设置，如果需要适配博客明暗模式更改统计图文字颜色，以 butterfly 主题为例，可以修改三处的 <code>color</code>:</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">- var color = &#x27;#000&#x27;</span></span><br><span class="line"><span class="addition">+ var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br></pre></td></tr></table></figure></p><p>同时引入自定义 js 文件并添加以下代码：</p><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">switchVisitChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 这里为了统一颜色选取的是“明暗模式”下的两种字体颜色，也可以自己定义</span></span><br><span class="line">  <span class="keyword">let</span> color = <span class="built_in">document</span>.documentElement.getAttribute(<span class="string">&#x27;data-theme&#x27;</span>) === <span class="string">&#x27;light&#x27;</span> ? <span class="string">&#x27;#4c4948&#x27;</span> : <span class="string">&#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;map-chart&#x27;</span>)) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> mapOptionNew = mapOption</span><br><span class="line">      mapOptionNew.title.textStyle.color = color</span><br><span class="line">      mapOptionNew.visualMap.textStyle.color = color</span><br><span class="line">      mapChart.setOption(mapOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;trends-chart&#x27;</span>)) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> trendsOptionNew = trendsOption</span><br><span class="line">      trendsOptionNew.title.textStyle.color = color</span><br><span class="line">      trendsOptionNew.xAxis.nameTextStyle.color = color</span><br><span class="line">      trendsOptionNew.yAxis.nameTextStyle.color = color</span><br><span class="line">      trendsOptionNew.xAxis.axisLabel.color = color</span><br><span class="line">      trendsOptionNew.yAxis.axisLabel.color = color</span><br><span class="line">      trendsOptionNew.xAxis.axisLine.lineStyle.color = color</span><br><span class="line">      trendsOptionNew.yAxis.axisLine.lineStyle.color = color</span><br><span class="line">      trendsOptionNew.series[<span class="number">0</span>].markLine.data[<span class="number">0</span>].label.color = color</span><br><span class="line">      trendsChart.setOption(trendsOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;sources-chart&#x27;</span>)) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> sourcesOptionNew = sourcesOption</span><br><span class="line">      sourcesOptionNew.title.textStyle.color = color</span><br><span class="line">      sourcesOptionNew.legend.textStyle.color = color</span><br><span class="line">      sourcesOptionNew.series[<span class="number">0</span>].label.color = color</span><br><span class="line">      sourcesChart.setOption(sourcesOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">document</span>.getElementById(<span class="string">&quot;mode-button&quot;</span>).addEventListener(<span class="string">&quot;click&quot;</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; <span class="built_in">setTimeout</span>(switchVisitChart, <span class="number">100</span>) &#125;)</span><br></pre></td></tr></table></figure></p><h2 id="Hexo-三连"><a href="#Hexo-三连" class="headerlink" title="Hexo 三连"></a>Hexo 三连</h2><p>执行 Hexo 三连</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo g &amp;&amp; hexo s</span><br></pre></td></tr></table></figure></p><h2 id="可能遇到的问题"><a href="#可能遇到的问题" class="headerlink" title="可能遇到的问题"></a>可能遇到的问题</h2><ul><li><p>控制台报错 <code>Uncaught ReferenceError: require is not defined</code></p><p><strong>解决方案：</strong></p><p>不能直接在页面引用 <code>charts.js</code>，是放在主题文件夹 <code>[Blogroot]\themes\butterfly\scripts\helpers\</code> 里面，<code>hexo g</code> 的时候会在 <code>div</code> 后面追加 echarts 图的 js。页面不需要引入 <code>charts.js</code>。</p></li><li><p>控制台报错 <code>Uncaught ReferenceError: echarts is not defined</code>。</p><p><strong>解决方案：</strong></p><p>需要在统计图的前引入 <code>echarts.js</code> 文件，最好是在页面的头部引入。</p></li><li><p>控制台报错 <code>Cannot find module &#39;cheerio&#39;</code>。</p><p><strong>解决方案：</strong></p><p>安装 <code>cheerio</code>，控制台执行 <code>npm i cheerio --save</code>。</p></li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      
      <comments>https://blog.eurkon.com/post/ef1da941.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Hexo 博客文章统计图</title>
      <link>https://blog.eurkon.com/post/1213ef82.html</link>
      <guid>https://blog.eurkon.com/post/1213ef82.html</guid>
      <pubDate>Thu, 01 Apr 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;本文教程主要针对 Hexo 博客，对博客的文章发布时间、文章分类、文章标签的维度绘制统计图，使用的是 &lt;a href=&quot;https://ec</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文教程主要针对 Hexo 博客，对博客的文章发布时间、文章分类、文章标签的维度绘制统计图，使用的是 <a href="https://echarts.apache.org/examples/zh/index.html">ECharts</a> 开源可视化库。具体效果可以点击本站的 <a href="/charts/">统计--文章统计</a> 页面查看。</p><blockquote><ul><li><code>2021-05-17</code> 新增主题“明暗模式”下统计图的颜色切换。</li><li><code>2021-11-30</code> 新增标签统计图和分类统计图的页面跳转功能。</li><li><code>2022-05-07</code> 新增文章发布统计图自定义统计开始日期属性 <code>data-start</code>。</li><li><code>2022-08-15</code> <strong>V2.0</strong> 适配多层级分类，单层分类时显示饼状图，多层分类时显示旭日图; 多分类时页面也可以跳转到子分类页面。</li></ul></blockquote><p>如果想绘制博客站点访问统计图的可以参考文章 <a href="/post/61763977.html">Hexo 博客实时访问统计图</a></p><h2 id="新建-charts-页面"><a href="#新建-charts-页面" class="headerlink" title="新建 charts 页面"></a>新建 charts 页面</h2><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab active">使用命令创建</button><button type="button" class="tab">手动创建</button></div><div class="tab-contents"><div class="tab-item-content active"><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo new page charts</span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content"><p>在 <code>[Blogroot]\source\</code> 目录下新建 <code>charts</code> 文件夹，并在新建的 <code>charts</code> 文件夹下新建 <code>index.md</code> 文件，添加以下内容：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: 文章统计</span><br><span class="line">date: 2020-03-01 08:00:00</span><br><span class="line">---</span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><h2 id="引入-ECharts-js"><a href="#引入-ECharts-js" class="headerlink" title="引入 ECharts.js"></a>引入 ECharts.js</h2><blockquote><p>echarts.js 必须在渲染 echarts 实例的 JavaScript 前引入。</p></blockquote><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab active">全局引入</button><button type="button" class="tab">页面引入</button></div><div class="tab-contents"><div class="tab-item-content active"><p>以 butterfly 主题为例，可以在 <code>[Blogroot]\_config.butterfly.yml</code> 的 <code>inject</code> 配置项中引入 <code>echart.js</code> 文件。</p><p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line">  <span class="attr">head:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&lt;script</span> <span class="string">src=&quot;https://npm.elemecdn.com/echarts@4.9.0/dist/echarts.min.js&quot;&gt;&lt;/script&gt;</span></span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content"><p>可以在 <code>index.md</code> 添加以下内容：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;https://npm.elemecdn.com/echarts@4.9.0/dist/echarts.min.js&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><h2 id="文章统计代码"><a href="#文章统计代码" class="headerlink" title="文章统计代码"></a>文章统计代码</h2><p>以 butterfly 主题为例，可以在 <code>[Blogroot]\themes\butterfly\scripts\helpers\</code> 目录下新建 <code>charts.js</code> 文件，然后添加以下内容：</p><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab">V1.0</button><button type="button" class="tab active">V2.0</button></div><div class="tab-contents"><div class="tab-item-content"><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> cheerio = <span class="built_in">require</span>(<span class="string">&#x27;cheerio&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> moment = <span class="built_in">require</span>(<span class="string">&#x27;moment&#x27;</span>)</span><br><span class="line"></span><br><span class="line">hexo.extend.filter.register(<span class="string">&#x27;after_render:html&#x27;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">locals</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> $ = cheerio.load(locals)</span><br><span class="line">  <span class="keyword">const</span> post = $(<span class="string">&#x27;#posts-chart&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> tag = $(<span class="string">&#x27;#tags-chart&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> category = $(<span class="string">&#x27;#categories-chart&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> htmlEncode = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (post.length &gt; <span class="number">0</span> || tag.length &gt; <span class="number">0</span> || category.length &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (post.length &gt; <span class="number">0</span> &amp;&amp; $(<span class="string">&#x27;#postsChart&#x27;</span>).length === <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (post.attr(<span class="string">&#x27;data-encode&#x27;</span>) === <span class="string">&#x27;true&#x27;</span>) htmlEncode = <span class="literal">true</span></span><br><span class="line">      post.after(postsChart(post.attr(<span class="string">&#x27;data-start&#x27;</span>)))</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (tag.length &gt; <span class="number">0</span> &amp;&amp; $(<span class="string">&#x27;#tagsChart&#x27;</span>).length === <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (tag.attr(<span class="string">&#x27;data-encode&#x27;</span>) === <span class="string">&#x27;true&#x27;</span>) htmlEncode = <span class="literal">true</span></span><br><span class="line">      tag.after(tagsChart(tag.attr(<span class="string">&#x27;data-length&#x27;</span>)))</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (category.length &gt; <span class="number">0</span> &amp;&amp; $(<span class="string">&#x27;#categoriesChart&#x27;</span>).length === <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (category.attr(<span class="string">&#x27;data-encode&#x27;</span>) === <span class="string">&#x27;true&#x27;</span>) htmlEncode = <span class="literal">true</span></span><br><span class="line">      category.after(categoriesChart())</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (htmlEncode) &#123;</span><br><span class="line">      <span class="keyword">return</span> $.root().html().replace(<span class="regexp">/&amp;amp;#/g</span>, <span class="string">&#x27;&amp;#&#x27;</span>)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> $.root().html()</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> locals</span><br><span class="line">  &#125;</span><br><span class="line">&#125;, <span class="number">15</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">postsChart</span> (<span class="params">startMonth</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> startDate = moment(startMonth || <span class="string">&#x27;2020-01&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> endDate = moment()</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> monthMap = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line">  <span class="keyword">const</span> dayTime = <span class="number">3600</span> * <span class="number">24</span> * <span class="number">1000</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> time = startDate; time &lt;= endDate; time += dayTime) &#123;</span><br><span class="line">    <span class="keyword">const</span> month = moment(time).format(<span class="string">&#x27;YYYY-MM&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> (!monthMap.has(month)) &#123;</span><br><span class="line">      monthMap.set(month, <span class="number">0</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  hexo.locals.get(<span class="string">&#x27;posts&#x27;</span>).forEach(<span class="function"><span class="keyword">function</span> (<span class="params">post</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> month = post.date.format(<span class="string">&#x27;YYYY-MM&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> (monthMap.has(month)) &#123;</span><br><span class="line">      monthMap.set(month, monthMap.get(month) + <span class="number">1</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="keyword">const</span> monthArr = <span class="built_in">JSON</span>.stringify([...monthMap.keys()])</span><br><span class="line">  <span class="keyword">const</span> monthValueArr = <span class="built_in">JSON</span>.stringify([...monthMap.values()])</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="string">`</span></span><br><span class="line"><span class="string">  &lt;script id=&quot;postsChart&quot;&gt;</span></span><br><span class="line"><span class="string">    var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line"><span class="string">    var postsChart = echarts.init(document.getElementById(&#x27;posts-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">    var postsOption = &#123;</span></span><br><span class="line"><span class="string">      title: &#123;</span></span><br><span class="line"><span class="string">        text: &#x27;文章发布统计图&#x27;,</span></span><br><span class="line"><span class="string">        x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">        textStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      tooltip: &#123;</span></span><br><span class="line"><span class="string">        trigger: &#x27;axis&#x27;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      xAxis: &#123;</span></span><br><span class="line"><span class="string">        name: &#x27;日期&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;category&#x27;,</span></span><br><span class="line"><span class="string">        boundaryGap: false,</span></span><br><span class="line"><span class="string">        nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisTick: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLabel: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLine: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          lineStyle: &#123;</span></span><br><span class="line"><span class="string">            color: color</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;monthArr&#125;</span></span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      yAxis: &#123;</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;value&#x27;,</span></span><br><span class="line"><span class="string">        nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        splitLine: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisTick: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLabel: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLine: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          lineStyle: &#123;</span></span><br><span class="line"><span class="string">            color: color</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      series: [&#123;</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;line&#x27;,</span></span><br><span class="line"><span class="string">        smooth: true,</span></span><br><span class="line"><span class="string">        lineStyle: &#123;</span></span><br><span class="line"><span class="string">            width: 0</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        showSymbol: false,</span></span><br><span class="line"><span class="string">        itemStyle: &#123;</span></span><br><span class="line"><span class="string">          opacity: 1,</span></span><br><span class="line"><span class="string">          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">            offset: 0,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(128, 255, 165)&#x27;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          &#123;</span></span><br><span class="line"><span class="string">            offset: 1,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(1, 191, 236)&#x27;</span></span><br><span class="line"><span class="string">          &#125;])</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        areaStyle: &#123;</span></span><br><span class="line"><span class="string">          opacity: 1,</span></span><br><span class="line"><span class="string">          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">            offset: 0,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(128, 255, 165)&#x27;</span></span><br><span class="line"><span class="string">          &#125;, &#123;</span></span><br><span class="line"><span class="string">            offset: 1,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(1, 191, 236)&#x27;</span></span><br><span class="line"><span class="string">          &#125;])</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;monthValueArr&#125;</span>,</span></span><br><span class="line"><span class="string">        markLine: &#123;</span></span><br><span class="line"><span class="string">          data: [&#123;</span></span><br><span class="line"><span class="string">            name: &#x27;平均值&#x27;,</span></span><br><span class="line"><span class="string">            type: &#x27;average&#x27;,</span></span><br><span class="line"><span class="string">            label: &#123;</span></span><br><span class="line"><span class="string">              color: color</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">          &#125;]</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;]</span></span><br><span class="line"><span class="string">    &#125;;</span></span><br><span class="line"><span class="string">    postsChart.setOption(postsOption);</span></span><br><span class="line"><span class="string">    window.addEventListener(&#x27;resize&#x27;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">      postsChart.resize();</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">  &lt;/script&gt;`</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">tagsChart</span> (<span class="params">len</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> tagArr = []</span><br><span class="line">  hexo.locals.get(<span class="string">&#x27;tags&#x27;</span>).map(<span class="function"><span class="keyword">function</span> (<span class="params">tag</span>) </span>&#123;</span><br><span class="line">    tagArr.push(&#123; <span class="attr">name</span>: tag.name, <span class="attr">value</span>: tag.length &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">  tagArr.sort(<span class="function">(<span class="params">a, b</span>) =&gt;</span> &#123; <span class="keyword">return</span> b.value - a.value &#125;)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> dataLength = <span class="built_in">Math</span>.min(tagArr.length, len) || tagArr.length</span><br><span class="line">  <span class="keyword">const</span> tagNameArr = []</span><br><span class="line">  <span class="keyword">const</span> tagCountArr = []</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; dataLength; i++) &#123;</span><br><span class="line">    tagNameArr.push(tagArr[i].name)</span><br><span class="line">    tagCountArr.push(tagArr[i].value)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> tagNameArrJson = <span class="built_in">JSON</span>.stringify(tagNameArr)</span><br><span class="line">  <span class="keyword">const</span> tagCountArrJson = <span class="built_in">JSON</span>.stringify(tagCountArr)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="string">`</span></span><br><span class="line"><span class="string">  &lt;script id=&quot;tagsChart&quot;&gt;</span></span><br><span class="line"><span class="string">    var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line"><span class="string">    var tagsChart = echarts.init(document.getElementById(&#x27;tags-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">    var tagsOption = &#123;</span></span><br><span class="line"><span class="string">      title: &#123;</span></span><br><span class="line"><span class="string">        text: &#x27;Top <span class="subst">$&#123;dataLength&#125;</span> 标签统计图&#x27;,</span></span><br><span class="line"><span class="string">        x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">        textStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      tooltip: &#123;&#125;,</span></span><br><span class="line"><span class="string">      xAxis: &#123;</span></span><br><span class="line"><span class="string">        name: &#x27;标签&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;category&#x27;,</span></span><br><span class="line"><span class="string">        nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisTick: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLabel: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLine: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          lineStyle: &#123;</span></span><br><span class="line"><span class="string">            color: color</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;tagNameArrJson&#125;</span></span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      yAxis: &#123;</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;value&#x27;,</span></span><br><span class="line"><span class="string">        splitLine: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisTick: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLabel: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLine: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          lineStyle: &#123;</span></span><br><span class="line"><span class="string">            color: color</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      series: [&#123;</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;bar&#x27;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;tagCountArrJson&#125;</span>,</span></span><br><span class="line"><span class="string">        itemStyle: &#123;</span></span><br><span class="line"><span class="string">          borderRadius: [5, 5, 0, 0],</span></span><br><span class="line"><span class="string">          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">            offset: 0,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(128, 255, 165)&#x27;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          &#123;</span></span><br><span class="line"><span class="string">            offset: 1,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(1, 191, 236)&#x27;</span></span><br><span class="line"><span class="string">          &#125;])</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        emphasis: &#123;</span></span><br><span class="line"><span class="string">          itemStyle: &#123;</span></span><br><span class="line"><span class="string">            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">              offset: 0,</span></span><br><span class="line"><span class="string">              color: &#x27;rgba(128, 255, 195)&#x27;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            &#123;</span></span><br><span class="line"><span class="string">              offset: 1,</span></span><br><span class="line"><span class="string">              color: &#x27;rgba(1, 211, 255)&#x27;</span></span><br><span class="line"><span class="string">            &#125;])</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        markLine: &#123;</span></span><br><span class="line"><span class="string">          data: [&#123;</span></span><br><span class="line"><span class="string">            name: &#x27;平均值&#x27;,</span></span><br><span class="line"><span class="string">            type: &#x27;average&#x27;,</span></span><br><span class="line"><span class="string">            label: &#123;</span></span><br><span class="line"><span class="string">              color: color</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">          &#125;]</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;]</span></span><br><span class="line"><span class="string">    &#125;;</span></span><br><span class="line"><span class="string">    tagsChart.setOption(tagsOption);</span></span><br><span class="line"><span class="string">    window.addEventListener(&#x27;resize&#x27;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">      tagsChart.resize();</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">    tagsChart.on(&#x27;click&#x27;, &#x27;series&#x27;, (event) =&gt; &#123;</span></span><br><span class="line"><span class="string">      if (event.name === &#x27;平均值&#x27;) return</span></span><br><span class="line"><span class="string">      let href = &#x27;/tags/&#x27; + event.name + &#x27;/&#x27;;</span></span><br><span class="line"><span class="string">      window.location.href = href;</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">  &lt;/script&gt;`</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">categoriesChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> categoryArr = []</span><br><span class="line">  hexo.locals.get(<span class="string">&#x27;categories&#x27;</span>).map(<span class="function"><span class="keyword">function</span> (<span class="params">category</span>) </span>&#123;</span><br><span class="line">    categoryArr.push(&#123; <span class="attr">name</span>: category.name, <span class="attr">value</span>: category.length &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">  categoryArr.sort(<span class="function">(<span class="params">a, b</span>) =&gt;</span> &#123; <span class="keyword">return</span> b.value - a.value &#125;);</span><br><span class="line">  <span class="keyword">const</span> categoryArrJson = <span class="built_in">JSON</span>.stringify(categoryArr)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="string">`</span></span><br><span class="line"><span class="string">  &lt;script id=&quot;categoriesChart&quot;&gt;</span></span><br><span class="line"><span class="string">    var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line"><span class="string">    var categoriesChart = echarts.init(document.getElementById(&#x27;categories-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">    var categoriesOption = &#123;</span></span><br><span class="line"><span class="string">      title: &#123;</span></span><br><span class="line"><span class="string">        text: &#x27;文章分类统计图&#x27;,</span></span><br><span class="line"><span class="string">        x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">        textStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      legend: &#123;</span></span><br><span class="line"><span class="string">        top: &#x27;bottom&#x27;,</span></span><br><span class="line"><span class="string">        textStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      tooltip: &#123;</span></span><br><span class="line"><span class="string">        trigger: &#x27;item&#x27;,</span></span><br><span class="line"><span class="string">        formatter: &#x27;&#123;a&#125; &lt;br/&gt;&#123;b&#125; : &#123;c&#125; (&#123;d&#125;%)&#x27;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      series: [&#123;</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;pie&#x27;,</span></span><br><span class="line"><span class="string">        radius: [30, 80],</span></span><br><span class="line"><span class="string">        center: [&#x27;50%&#x27;, &#x27;50%&#x27;],</span></span><br><span class="line"><span class="string">        roseType: &#x27;area&#x27;,</span></span><br><span class="line"><span class="string">        label: &#123;</span></span><br><span class="line"><span class="string">          color: color,</span></span><br><span class="line"><span class="string">          formatter: &#x27;&#123;b&#125; : &#123;c&#125; (&#123;d&#125;%)&#x27;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;categoryArrJson&#125;</span>,</span></span><br><span class="line"><span class="string">        itemStyle: &#123;</span></span><br><span class="line"><span class="string">          emphasis: &#123;</span></span><br><span class="line"><span class="string">            shadowBlur: 10,</span></span><br><span class="line"><span class="string">            shadowOffsetX: 0,</span></span><br><span class="line"><span class="string">            shadowColor: &#x27;rgba(255, 255, 255, 0.5)&#x27;</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;]</span></span><br><span class="line"><span class="string">    &#125;;</span></span><br><span class="line"><span class="string">    categoriesChart.setOption(categoriesOption);</span></span><br><span class="line"><span class="string">    window.addEventListener(&#x27;resize&#x27;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">      categoriesChart.resize();</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">    categoriesChart.on(&#x27;click&#x27;, &#x27;series&#x27;, (event) =&gt; &#123;</span></span><br><span class="line"><span class="string">      let href = &#x27;/categories/&#x27; + event.name + &#x27;/&#x27;;</span></span><br><span class="line"><span class="string">      window.location.href = href;</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">  &lt;/script&gt;`</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content active"><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> cheerio = <span class="built_in">require</span>(<span class="string">&#x27;cheerio&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> moment = <span class="built_in">require</span>(<span class="string">&#x27;moment&#x27;</span>)</span><br><span class="line"></span><br><span class="line">hexo.extend.filter.register(<span class="string">&#x27;after_render:html&#x27;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">locals</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> $ = cheerio.load(locals)</span><br><span class="line">  <span class="keyword">const</span> post = $(<span class="string">&#x27;#posts-chart&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> tag = $(<span class="string">&#x27;#tags-chart&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> category = $(<span class="string">&#x27;#categories-chart&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> htmlEncode = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (post.length &gt; <span class="number">0</span> || tag.length &gt; <span class="number">0</span> || category.length &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (post.length &gt; <span class="number">0</span> &amp;&amp; $(<span class="string">&#x27;#postsChart&#x27;</span>).length === <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (post.attr(<span class="string">&#x27;data-encode&#x27;</span>) === <span class="string">&#x27;true&#x27;</span>) htmlEncode = <span class="literal">true</span></span><br><span class="line">      post.after(postsChart(post.attr(<span class="string">&#x27;data-start&#x27;</span>)))</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (tag.length &gt; <span class="number">0</span> &amp;&amp; $(<span class="string">&#x27;#tagsChart&#x27;</span>).length === <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (tag.attr(<span class="string">&#x27;data-encode&#x27;</span>) === <span class="string">&#x27;true&#x27;</span>) htmlEncode = <span class="literal">true</span></span><br><span class="line">      tag.after(tagsChart(tag.attr(<span class="string">&#x27;data-length&#x27;</span>)))</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (category.length &gt; <span class="number">0</span> &amp;&amp; $(<span class="string">&#x27;#categoriesChart&#x27;</span>).length === <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (category.attr(<span class="string">&#x27;data-encode&#x27;</span>) === <span class="string">&#x27;true&#x27;</span>) htmlEncode = <span class="literal">true</span></span><br><span class="line">      category.after(categoriesChart(category.attr(<span class="string">&#x27;data-parent&#x27;</span>)))</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (htmlEncode) &#123;</span><br><span class="line">      <span class="keyword">return</span> $.root().html().replace(<span class="regexp">/&amp;amp;#/g</span>, <span class="string">&#x27;&amp;#&#x27;</span>)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> $.root().html()</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> locals</span><br><span class="line">  &#125;</span><br><span class="line">&#125;, <span class="number">15</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">postsChart</span> (<span class="params">startMonth</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> startDate = moment(startMonth || <span class="string">&#x27;2020-01&#x27;</span>)</span><br><span class="line">  <span class="keyword">const</span> endDate = moment()</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> monthMap = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line">  <span class="keyword">const</span> dayTime = <span class="number">3600</span> * <span class="number">24</span> * <span class="number">1000</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> time = startDate; time &lt;= endDate; time += dayTime) &#123;</span><br><span class="line">    <span class="keyword">const</span> month = moment(time).format(<span class="string">&#x27;YYYY-MM&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> (!monthMap.has(month)) &#123;</span><br><span class="line">      monthMap.set(month, <span class="number">0</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  hexo.locals.get(<span class="string">&#x27;posts&#x27;</span>).forEach(<span class="function"><span class="keyword">function</span> (<span class="params">post</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> month = post.date.format(<span class="string">&#x27;YYYY-MM&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> (monthMap.has(month)) &#123;</span><br><span class="line">      monthMap.set(month, monthMap.get(month) + <span class="number">1</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="keyword">const</span> monthArr = <span class="built_in">JSON</span>.stringify([...monthMap.keys()])</span><br><span class="line">  <span class="keyword">const</span> monthValueArr = <span class="built_in">JSON</span>.stringify([...monthMap.values()])</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="string">`</span></span><br><span class="line"><span class="string">  &lt;script id=&quot;postsChart&quot;&gt;</span></span><br><span class="line"><span class="string">    var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line"><span class="string">    var postsChart = echarts.init(document.getElementById(&#x27;posts-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">    var postsOption = &#123;</span></span><br><span class="line"><span class="string">      title: &#123;</span></span><br><span class="line"><span class="string">        text: &#x27;文章发布统计图&#x27;,</span></span><br><span class="line"><span class="string">        x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">        textStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      tooltip: &#123;</span></span><br><span class="line"><span class="string">        trigger: &#x27;axis&#x27;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      xAxis: &#123;</span></span><br><span class="line"><span class="string">        name: &#x27;日期&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;category&#x27;,</span></span><br><span class="line"><span class="string">        boundaryGap: false,</span></span><br><span class="line"><span class="string">        nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisTick: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLabel: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLine: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          lineStyle: &#123;</span></span><br><span class="line"><span class="string">            color: color</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;monthArr&#125;</span></span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      yAxis: &#123;</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;value&#x27;,</span></span><br><span class="line"><span class="string">        nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        splitLine: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisTick: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLabel: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLine: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          lineStyle: &#123;</span></span><br><span class="line"><span class="string">            color: color</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      series: [&#123;</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;line&#x27;,</span></span><br><span class="line"><span class="string">        smooth: true,</span></span><br><span class="line"><span class="string">        lineStyle: &#123;</span></span><br><span class="line"><span class="string">            width: 0</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        showSymbol: false,</span></span><br><span class="line"><span class="string">        itemStyle: &#123;</span></span><br><span class="line"><span class="string">          opacity: 1,</span></span><br><span class="line"><span class="string">          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">            offset: 0,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(128, 255, 165)&#x27;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          &#123;</span></span><br><span class="line"><span class="string">            offset: 1,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(1, 191, 236)&#x27;</span></span><br><span class="line"><span class="string">          &#125;])</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        areaStyle: &#123;</span></span><br><span class="line"><span class="string">          opacity: 1,</span></span><br><span class="line"><span class="string">          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">            offset: 0,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(128, 255, 165)&#x27;</span></span><br><span class="line"><span class="string">          &#125;, &#123;</span></span><br><span class="line"><span class="string">            offset: 1,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(1, 191, 236)&#x27;</span></span><br><span class="line"><span class="string">          &#125;])</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;monthValueArr&#125;</span>,</span></span><br><span class="line"><span class="string">        markLine: &#123;</span></span><br><span class="line"><span class="string">          data: [&#123;</span></span><br><span class="line"><span class="string">            name: &#x27;平均值&#x27;,</span></span><br><span class="line"><span class="string">            type: &#x27;average&#x27;,</span></span><br><span class="line"><span class="string">            label: &#123;</span></span><br><span class="line"><span class="string">              color: color</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">          &#125;]</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;]</span></span><br><span class="line"><span class="string">    &#125;;</span></span><br><span class="line"><span class="string">    postsChart.setOption(postsOption);</span></span><br><span class="line"><span class="string">    window.addEventListener(&#x27;resize&#x27;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">      postsChart.resize();</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">    postsChart.on(&#x27;click&#x27;, &#x27;series&#x27;, (event) =&gt; &#123;</span></span><br><span class="line"><span class="string">      if (event.componentType === &#x27;series&#x27;) window.location.href = &#x27;/archives/&#x27; + event.name.replace(&#x27;-&#x27;, &#x27;/&#x27;);</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">  &lt;/script&gt;`</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">tagsChart</span> (<span class="params">len</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> tagArr = []</span><br><span class="line">  hexo.locals.get(<span class="string">&#x27;tags&#x27;</span>).map(<span class="function"><span class="keyword">function</span> (<span class="params">tag</span>) </span>&#123;</span><br><span class="line">    tagArr.push(&#123; <span class="attr">name</span>: tag.name, <span class="attr">value</span>: tag.length, <span class="attr">path</span>: tag.path &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">  tagArr.sort(<span class="function">(<span class="params">a, b</span>) =&gt;</span> &#123; <span class="keyword">return</span> b.value - a.value &#125;)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> dataLength = <span class="built_in">Math</span>.min(tagArr.length, len) || tagArr.length</span><br><span class="line">  <span class="keyword">const</span> tagNameArr = []</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; dataLength; i++) &#123;</span><br><span class="line">    tagNameArr.push(tagArr[i].name)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> tagNameArrJson = <span class="built_in">JSON</span>.stringify(tagNameArr)</span><br><span class="line">  <span class="keyword">const</span> tagArrJson = <span class="built_in">JSON</span>.stringify(tagArr)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="string">`</span></span><br><span class="line"><span class="string">  &lt;script id=&quot;tagsChart&quot;&gt;</span></span><br><span class="line"><span class="string">    var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line"><span class="string">    var tagsChart = echarts.init(document.getElementById(&#x27;tags-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">    var tagsOption = &#123;</span></span><br><span class="line"><span class="string">      title: &#123;</span></span><br><span class="line"><span class="string">        text: &#x27;Top <span class="subst">$&#123;dataLength&#125;</span> 标签统计图&#x27;,</span></span><br><span class="line"><span class="string">        x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">        textStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      tooltip: &#123;&#125;,</span></span><br><span class="line"><span class="string">      xAxis: &#123;</span></span><br><span class="line"><span class="string">        name: &#x27;标签&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;category&#x27;,</span></span><br><span class="line"><span class="string">        nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisTick: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLabel: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          color: color,</span></span><br><span class="line"><span class="string">          interval: 0</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLine: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          lineStyle: &#123;</span></span><br><span class="line"><span class="string">            color: color</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;tagNameArrJson&#125;</span></span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      yAxis: &#123;</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;value&#x27;,</span></span><br><span class="line"><span class="string">        splitLine: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        nameTextStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisTick: &#123;</span></span><br><span class="line"><span class="string">          show: false</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLabel: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        axisLine: &#123;</span></span><br><span class="line"><span class="string">          show: true,</span></span><br><span class="line"><span class="string">          lineStyle: &#123;</span></span><br><span class="line"><span class="string">            color: color</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      series: [&#123;</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;bar&#x27;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;tagArrJson&#125;</span>,</span></span><br><span class="line"><span class="string">        itemStyle: &#123;</span></span><br><span class="line"><span class="string">          borderRadius: [5, 5, 0, 0],</span></span><br><span class="line"><span class="string">          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">            offset: 0,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(128, 255, 165)&#x27;</span></span><br><span class="line"><span class="string">          &#125;,</span></span><br><span class="line"><span class="string">          &#123;</span></span><br><span class="line"><span class="string">            offset: 1,</span></span><br><span class="line"><span class="string">            color: &#x27;rgba(1, 191, 236)&#x27;</span></span><br><span class="line"><span class="string">          &#125;])</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        emphasis: &#123;</span></span><br><span class="line"><span class="string">          itemStyle: &#123;</span></span><br><span class="line"><span class="string">            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [&#123;</span></span><br><span class="line"><span class="string">              offset: 0,</span></span><br><span class="line"><span class="string">              color: &#x27;rgba(128, 255, 195)&#x27;</span></span><br><span class="line"><span class="string">            &#125;,</span></span><br><span class="line"><span class="string">            &#123;</span></span><br><span class="line"><span class="string">              offset: 1,</span></span><br><span class="line"><span class="string">              color: &#x27;rgba(1, 211, 255)&#x27;</span></span><br><span class="line"><span class="string">            &#125;])</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        markLine: &#123;</span></span><br><span class="line"><span class="string">          data: [&#123;</span></span><br><span class="line"><span class="string">            name: &#x27;平均值&#x27;,</span></span><br><span class="line"><span class="string">            type: &#x27;average&#x27;,</span></span><br><span class="line"><span class="string">            label: &#123;</span></span><br><span class="line"><span class="string">              color: color</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">          &#125;]</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;]</span></span><br><span class="line"><span class="string">    &#125;;</span></span><br><span class="line"><span class="string">    tagsChart.setOption(tagsOption);</span></span><br><span class="line"><span class="string">    window.addEventListener(&#x27;resize&#x27;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">      tagsChart.resize();</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">    tagsChart.on(&#x27;click&#x27;, &#x27;series&#x27;, (event) =&gt; &#123;</span></span><br><span class="line"><span class="string">      if(event.data.path) window.location.href = &#x27;/&#x27; + event.data.path;</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">  &lt;/script&gt;`</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">categoriesChart</span> (<span class="params">dataParent</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> categoryArr = []</span><br><span class="line">  <span class="keyword">let</span> categoryParentFlag = <span class="literal">false</span></span><br><span class="line">  hexo.locals.get(<span class="string">&#x27;categories&#x27;</span>).map(<span class="function"><span class="keyword">function</span> (<span class="params">category</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (category.parent) categoryParentFlag = <span class="literal">true</span></span><br><span class="line">    categoryArr.push(&#123;</span><br><span class="line">      name: category.name,</span><br><span class="line">      value: category.length,</span><br><span class="line">      path: category.path,</span><br><span class="line">      id: category._id,</span><br><span class="line">      parentId: category.parent || <span class="string">&#x27;0&#x27;</span></span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">  categoryParentFlag = categoryParentFlag &amp;&amp; dataParent === <span class="string">&#x27;true&#x27;</span></span><br><span class="line">  categoryArr.sort(<span class="function">(<span class="params">a, b</span>) =&gt;</span> &#123; <span class="keyword">return</span> b.value - a.value &#125;)</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">translateListToTree</span> (<span class="params">data, parent</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> tree = []</span><br><span class="line">    <span class="keyword">let</span> temp</span><br><span class="line">    data.forEach(<span class="function">(<span class="params">item, index</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (data[index].parentId == parent) &#123;</span><br><span class="line">        <span class="keyword">let</span> obj = data[index];</span><br><span class="line">        temp = translateListToTree(data, data[index].id);</span><br><span class="line">        <span class="keyword">if</span> (temp.length &gt; <span class="number">0</span>) &#123;</span><br><span class="line">          obj.children = temp</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (tree.indexOf())</span><br><span class="line">          tree.push(obj)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">    <span class="keyword">return</span> tree</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> categoryNameJson = <span class="built_in">JSON</span>.stringify(categoryArr.map(<span class="function"><span class="keyword">function</span> (<span class="params">category</span>) </span>&#123; <span class="keyword">return</span> category.name &#125;))</span><br><span class="line">  <span class="keyword">const</span> categoryArrJson = <span class="built_in">JSON</span>.stringify(categoryArr)</span><br><span class="line">  <span class="keyword">const</span> categoryArrParentJson = <span class="built_in">JSON</span>.stringify(translateListToTree(categoryArr, <span class="string">&#x27;0&#x27;</span>))</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="string">`</span></span><br><span class="line"><span class="string">  &lt;script id=&quot;categoriesChart&quot;&gt;</span></span><br><span class="line"><span class="string">    var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line"><span class="string">    var categoriesChart = echarts.init(document.getElementById(&#x27;categories-chart&#x27;), &#x27;light&#x27;);</span></span><br><span class="line"><span class="string">    var categoryParentFlag = <span class="subst">$&#123;categoryParentFlag&#125;</span></span></span><br><span class="line"><span class="string">    var categoriesOption = &#123;</span></span><br><span class="line"><span class="string">      title: &#123;</span></span><br><span class="line"><span class="string">        text: &#x27;文章分类统计图&#x27;,</span></span><br><span class="line"><span class="string">        x: &#x27;center&#x27;,</span></span><br><span class="line"><span class="string">        textStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      legend: &#123;</span></span><br><span class="line"><span class="string">        top: &#x27;bottom&#x27;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;categoryNameJson&#125;</span>,</span></span><br><span class="line"><span class="string">        textStyle: &#123;</span></span><br><span class="line"><span class="string">          color: color</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      tooltip: &#123;</span></span><br><span class="line"><span class="string">        trigger: &#x27;item&#x27;</span></span><br><span class="line"><span class="string">      &#125;,</span></span><br><span class="line"><span class="string">      series: []</span></span><br><span class="line"><span class="string">    &#125;;</span></span><br><span class="line"><span class="string">    categoriesOption.series.push(</span></span><br><span class="line"><span class="string">      categoryParentFlag ? </span></span><br><span class="line"><span class="string">      &#123;</span></span><br><span class="line"><span class="string">        nodeClick :false,</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;sunburst&#x27;,</span></span><br><span class="line"><span class="string">        radius: [&#x27;15%&#x27;, &#x27;90%&#x27;],</span></span><br><span class="line"><span class="string">        center: [&#x27;50%&#x27;, &#x27;55%&#x27;],</span></span><br><span class="line"><span class="string">        sort: &#x27;desc&#x27;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;categoryArrParentJson&#125;</span>,</span></span><br><span class="line"><span class="string">        itemStyle: &#123;</span></span><br><span class="line"><span class="string">          borderColor: &#x27;#fff&#x27;,</span></span><br><span class="line"><span class="string">          borderWidth: 2,</span></span><br><span class="line"><span class="string">          emphasis: &#123;</span></span><br><span class="line"><span class="string">            focus: &#x27;ancestor&#x27;,</span></span><br><span class="line"><span class="string">            shadowBlur: 10,</span></span><br><span class="line"><span class="string">            shadowOffsetX: 0,</span></span><br><span class="line"><span class="string">            shadowColor: &#x27;rgba(255, 255, 255, 0.5)&#x27;</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;</span></span><br><span class="line"><span class="string">      :</span></span><br><span class="line"><span class="string">      &#123;</span></span><br><span class="line"><span class="string">        name: &#x27;文章篇数&#x27;,</span></span><br><span class="line"><span class="string">        type: &#x27;pie&#x27;,</span></span><br><span class="line"><span class="string">        radius: [30, 80],</span></span><br><span class="line"><span class="string">        roseType: &#x27;area&#x27;,</span></span><br><span class="line"><span class="string">        label: &#123;</span></span><br><span class="line"><span class="string">          color: color,</span></span><br><span class="line"><span class="string">          formatter: &#x27;&#123;b&#125; : &#123;c&#125; (&#123;d&#125;%)&#x27;</span></span><br><span class="line"><span class="string">        &#125;,</span></span><br><span class="line"><span class="string">        data: <span class="subst">$&#123;categoryArrJson&#125;</span>,</span></span><br><span class="line"><span class="string">        itemStyle: &#123;</span></span><br><span class="line"><span class="string">          emphasis: &#123;</span></span><br><span class="line"><span class="string">            shadowBlur: 10,</span></span><br><span class="line"><span class="string">            shadowOffsetX: 0,</span></span><br><span class="line"><span class="string">            shadowColor: &#x27;rgba(255, 255, 255, 0.5)&#x27;</span></span><br><span class="line"><span class="string">          &#125;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">      &#125;</span></span><br><span class="line"><span class="string">    )</span></span><br><span class="line"><span class="string">    categoriesChart.setOption(categoriesOption);</span></span><br><span class="line"><span class="string">    window.addEventListener(&#x27;resize&#x27;, () =&gt; &#123; </span></span><br><span class="line"><span class="string">      categoriesChart.resize();</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">    categoriesChart.on(&#x27;click&#x27;, &#x27;series&#x27;, (event) =&gt; &#123;</span></span><br><span class="line"><span class="string">      if(event.data.path) window.location.href = &#x27;/&#x27; + event.data.path;</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">  &lt;/script&gt;`</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><p>更多统计图的自定义属性可以查看 <a href="https://echarts.apache.org/zh/option.html">ECharts 配置项文档</a>，根据自行喜好对 ECharts 统计图进行修改。</p><h2 id="使用统计图"><a href="#使用统计图" class="headerlink" title="使用统计图"></a>使用统计图</h2><p>在上文新建的 <code>[Blogroot]\source\charts\index.md</code> 文件中添加以下内容：</p><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab">V1.0</button><button type="button" class="tab active">V2.0</button></div><div class="tab-contents"><div class="tab-item-content"><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 文章发布时间统计图 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;posts-chart&quot;</span> <span class="attr">data-start</span>=<span class="string">&quot;2021-01&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 300px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&lt;!-- 文章标签统计图 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;tags-chart&quot;</span> <span class="attr">data-length</span>=<span class="string">&quot;10&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 300px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&lt;!-- 文章分类统计图 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;categories-chart&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 300px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content active"><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 文章发布时间统计图 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;posts-chart&quot;</span> <span class="attr">data-start</span>=<span class="string">&quot;2021-01&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 300px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&lt;!-- 文章标签统计图 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;tags-chart&quot;</span> <span class="attr">data-length</span>=<span class="string">&quot;10&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 300px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">&lt;!-- 文章分类统计图 --&gt;</span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;categories-chart&quot;</span> <span class="attr">data-parent</span>=<span class="string">&quot;true&quot;</span> <span class="attr">style</span>=<span class="string">&quot;border-radius: 8px; height: 300px; padding: 10px;&quot;</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><p>当然也可以在其他页面引入文章统计图，如果出现图表显示不全的现象可以修改 <code>div</code> 的 <code>height</code> 属性。。</p><ul><li><code>posts-chart</code> 的 <code>data-start=&quot;2021-01&quot;</code> 属性表示文章发布时间统计图仅显示 <code>2021-01</code> 及以后的文章数据。</li><li><code>tags-chart</code> 的 <code>data-length=&quot;10&quot;</code> 属性表示仅显示排名前 <code>10</code> 的标签。</li><li><code>categories-chart</code> 的 <code>data-parent=&quot;true&quot;</code> 属性表示 <code>有子分类</code> 时以旭日图显示分类，其他 <code>无子分类</code> 或 <code>设置为false</code> 或 <code>不设置该属性</code> 或 <code>设置为其他非true属性</code> 情况都以饼状图显示分类。</li></ul><p><strong>具体效果如下图所示：</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_chart/categories_sun.png" alt="多层分类旭日图"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/hexo_chart/categories_pie.png" alt="分类饼状图"></p><h3 id="在归档页使用统计图"><a href="#在归档页使用统计图" class="headerlink" title="在归档页使用统计图"></a>在归档页使用统计图</h3><p><code>[Blogroot]\themes\butterfly\layout\archive.pug</code></p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">extends includes/layout.pug</span><br><span class="line"></span><br><span class="line">block content</span><br><span class="line">  include ./includes/mixins/article-sort.pug</span><br><span class="line">  #archive</span><br><span class="line"><span class="addition">+   &lt;div id=&quot;posts-chart&quot; data-start=&quot;2021-01&quot; style=&quot;height: 300px; padding: 10px;&quot;&gt;&lt;/div&gt;</span></span><br></pre></td></tr></table></figure></p><p>或者写成 pug 文件语法 <code>#posts-chart(data-start=&quot;2021-01&quot; style=&quot;height: 300px; padding: 10px;&quot;)</code>。</p><h3 id="在分类页使用统计图"><a href="#在分类页使用统计图" class="headerlink" title="在分类页使用统计图"></a>在分类页使用统计图</h3><p>总分类页 <code>/categories</code>，在 <code>[Blogroot]\themes\butterfly\layout\includes\page\categories.pug</code> 添加：</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">  .category-lists!= list_categories()</span><br><span class="line"><span class="addition">+ &lt;div id=&quot;categories-chart&quot; data-parent=&quot;true&quot; style=&quot;height: 300px; padding: 10px;&quot;&gt;&lt;/div&gt;</span></span><br></pre></td></tr></table></figure></p><p>各分类页 <code>/categories/[分类]</code>，在 <code>[Blogroot]\themes\butterfly\layout\category.pug</code> 添加：</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">extends includes/layout.pug</span><br><span class="line"></span><br><span class="line">block content</span><br><span class="line">  if theme.category_ui == &#x27;index&#x27;</span><br><span class="line">    include ./includes/mixins/post-ui.pug</span><br><span class="line">    #recent-posts.recent-posts.category_ui</span><br><span class="line">      +postUI</span><br><span class="line">      include includes/pagination.pug</span><br><span class="line">  else</span><br><span class="line">    include ./includes/mixins/article-sort.pug</span><br><span class="line">    #category</span><br><span class="line"><span class="addition">+     &lt;div id=&quot;categories-chart&quot; data-parent=&quot;true&quot; style=&quot;height: 300px; padding: 10px;&quot;&gt;&lt;/div&gt;</span></span><br></pre></td></tr></table></figure></p><p>或者写成 pug 文件语法 <code>#categories-chart(data-parent=&quot;true&quot; style=&quot;height: 300px; padding: 10px;&quot;)</code>。</p><h3 id="在标签页使用统计图"><a href="#在标签页使用统计图" class="headerlink" title="在标签页使用统计图"></a>在标签页使用统计图</h3><p>总标签页 <code>/tags</code>，在 <code>[Blogroot]\themes\butterfly\layout\includes\page\tags.pug</code> 添加：</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">  .tag-cloud-list.is-center</span><br><span class="line">    !=cloudTags(&#123;source: site.tags, orderby: page.orderby || &#x27;random&#x27;, order: page.order || 1, minfontsize: 1.2, maxfontsize: 2.1, limit: 0, unit: &#x27;em&#x27;&#125;)</span><br><span class="line"><span class="addition">+ &lt;div id=&quot;tags-chart&quot; data-length=&quot;10&quot; style=&quot;height: 300px; padding: 10px;&quot;&gt;&lt;/div&gt;</span></span><br></pre></td></tr></table></figure></p><p>各标签页 <code>/tags/[标签]</code>，在 <code>[Blogroot]\themes\butterfly\layout\tag.pug</code> 添加：</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">extends includes/layout.pug</span><br><span class="line"></span><br><span class="line">block content</span><br><span class="line">  if theme.tag_ui == &#x27;index&#x27;</span><br><span class="line">    include ./includes/mixins/post-ui.pug</span><br><span class="line">    #recent-posts.recent-posts</span><br><span class="line">      +postUI</span><br><span class="line">      include includes/pagination.pug</span><br><span class="line">  else</span><br><span class="line">    include ./includes/mixins/article-sort.pug</span><br><span class="line">    #tag</span><br><span class="line"><span class="addition">+     &lt;div id=&quot;tags-chart&quot; data-length=&quot;10&quot; style=&quot;height: 300px; padding: 10px;&quot;&gt;&lt;/div&gt;</span></span><br></pre></td></tr></table></figure></p><p>或者写成 pug 文件语法 <code>#tags-chart(data-length=&quot;10&quot; style=&quot;height: 300px; padding: 10px;&quot;)</code>。</p><h2 id="适配明暗模式"><a href="#适配明暗模式" class="headerlink" title="适配明暗模式"></a>适配明暗模式</h2><p>统计图内部文字的颜色通过 <code>charts.js</code> 文件中的 <code>var color = &#39;#000&#39;</code> 设置，如果需要适配博客明暗模式更改统计图文字颜色，以 butterfly 主题为例，可以修改三处的 <code>color</code>:</p><p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">- var color = &#x27;#000&#x27;</span></span><br><span class="line"><span class="addition">+ var color = document.documentElement.getAttribute(&#x27;data-theme&#x27;) === &#x27;light&#x27; ? &#x27;#4c4948&#x27; : &#x27;rgba(255,255,255,0.7)&#x27;</span></span><br></pre></td></tr></table></figure></p><p>同时引入自定义 js 文件并添加以下代码：</p><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab">V1.0</button><button type="button" class="tab active">V2.0</button></div><div class="tab-contents"><div class="tab-item-content"><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">switchPostChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 这里为了统一颜色选取的是“明暗模式”下的两种字体颜色，也可以自己定义</span></span><br><span class="line">  <span class="keyword">let</span> color = <span class="built_in">document</span>.documentElement.getAttribute(<span class="string">&#x27;data-theme&#x27;</span>) === <span class="string">&#x27;light&#x27;</span> ? <span class="string">&#x27;#4c4948&#x27;</span> : <span class="string">&#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;posts-chart&#x27;</span>) &amp;&amp; postsOption) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> postsOptionNew = postsOption</span><br><span class="line">      postsOptionNew.title.textStyle.color = color</span><br><span class="line">      postsOptionNew.xAxis.nameTextStyle.color = color</span><br><span class="line">      postsOptionNew.yAxis.nameTextStyle.color = color</span><br><span class="line">      postsOptionNew.xAxis.axisLabel.color = color</span><br><span class="line">      postsOptionNew.yAxis.axisLabel.color = color</span><br><span class="line">      postsOptionNew.xAxis.axisLine.lineStyle.color = color</span><br><span class="line">      postsOptionNew.yAxis.axisLine.lineStyle.color = color</span><br><span class="line">      postsOptionNew.series[<span class="number">0</span>].markLine.data[<span class="number">0</span>].label.color = color</span><br><span class="line">      postsChart.setOption(postsOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;tags-chart&#x27;</span>) &amp;&amp; tagsOption) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> tagsOptionNew = tagsOption</span><br><span class="line">      tagsOptionNew.title.textStyle.color = color</span><br><span class="line">      tagsOptionNew.xAxis.nameTextStyle.color = color</span><br><span class="line">      tagsOptionNew.yAxis.nameTextStyle.color = color</span><br><span class="line">      tagsOptionNew.xAxis.axisLabel.color = color</span><br><span class="line">      tagsOptionNew.yAxis.axisLabel.color = color</span><br><span class="line">      tagsOptionNew.xAxis.axisLine.lineStyle.color = color</span><br><span class="line">      tagsOptionNew.yAxis.axisLine.lineStyle.color = color</span><br><span class="line">      tagsOptionNew.series[<span class="number">0</span>].markLine.data[<span class="number">0</span>].label.color = color</span><br><span class="line">      tagsChart.setOption(tagsOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;categories-chart&#x27;</span>) &amp;&amp; categoriesOption) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> categoriesOptionNew = categoriesOption</span><br><span class="line">      categoriesOptionNew.title.textStyle.color = color</span><br><span class="line">      categoriesOptionNew.legend.textStyle.color = color</span><br><span class="line">      categoriesOptionNew.series[<span class="number">0</span>].label.color = color</span><br><span class="line">      categoriesChart.setOption(categoriesOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">document</span>.getElementById(<span class="string">&quot;mode-button&quot;</span>).addEventListener(<span class="string">&quot;click&quot;</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; <span class="built_in">setTimeout</span>(switchPostChart, <span class="number">100</span>) &#125;)</span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content active"><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">switchPostChart</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 这里为了统一颜色选取的是“明暗模式”下的两种字体颜色，也可以自己定义</span></span><br><span class="line">  <span class="keyword">let</span> color = <span class="built_in">document</span>.documentElement.getAttribute(<span class="string">&#x27;data-theme&#x27;</span>) === <span class="string">&#x27;light&#x27;</span> ? <span class="string">&#x27;#4C4948&#x27;</span> : <span class="string">&#x27;rgba(255,255,255,0.7)&#x27;</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;posts-chart&#x27;</span>) &amp;&amp; postsOption) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> postsOptionNew = postsOption</span><br><span class="line">      postsOptionNew.title.textStyle.color = color</span><br><span class="line">      postsOptionNew.xAxis.nameTextStyle.color = color</span><br><span class="line">      postsOptionNew.yAxis.nameTextStyle.color = color</span><br><span class="line">      postsOptionNew.xAxis.axisLabel.color = color</span><br><span class="line">      postsOptionNew.yAxis.axisLabel.color = color</span><br><span class="line">      postsOptionNew.xAxis.axisLine.lineStyle.color = color</span><br><span class="line">      postsOptionNew.yAxis.axisLine.lineStyle.color = color</span><br><span class="line">      postsOptionNew.series[<span class="number">0</span>].markLine.data[<span class="number">0</span>].label.color = color</span><br><span class="line">      postsChart.setOption(postsOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;tags-chart&#x27;</span>) &amp;&amp; tagsOption) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> tagsOptionNew = tagsOption</span><br><span class="line">      tagsOptionNew.title.textStyle.color = color</span><br><span class="line">      tagsOptionNew.xAxis.nameTextStyle.color = color</span><br><span class="line">      tagsOptionNew.yAxis.nameTextStyle.color = color</span><br><span class="line">      tagsOptionNew.xAxis.axisLabel.color = color</span><br><span class="line">      tagsOptionNew.yAxis.axisLabel.color = color</span><br><span class="line">      tagsOptionNew.xAxis.axisLine.lineStyle.color = color</span><br><span class="line">      tagsOptionNew.yAxis.axisLine.lineStyle.color = color</span><br><span class="line">      tagsOptionNew.series[<span class="number">0</span>].markLine.data[<span class="number">0</span>].label.color = color</span><br><span class="line">      tagsChart.setOption(tagsOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById(<span class="string">&#x27;categories-chart&#x27;</span>) &amp;&amp; categoriesOption) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> categoriesOptionNew = categoriesOption</span><br><span class="line">      categoriesOptionNew.title.textStyle.color = color</span><br><span class="line">      categoriesOptionNew.legend.textStyle.color = color</span><br><span class="line">      <span class="keyword">if</span> (!categoryParentFlag) &#123; categoriesOptionNew.series[<span class="number">0</span>].label.color = color &#125;</span><br><span class="line">      categoriesChart.setOption(categoriesOptionNew)</span><br><span class="line">    &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">document</span>.getElementById(<span class="string">&quot;mode-button&quot;</span>).addEventListener(<span class="string">&quot;click&quot;</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; <span class="built_in">setTimeout</span>(switchPostChart, <span class="number">100</span>) &#125;)</span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><h2 id="增加点击跳转功能"><a href="#增加点击跳转功能" class="headerlink" title="增加点击跳转功能"></a>增加点击跳转功能</h2><p><strong>注：上面的章节已经包含此段代码，此章节只用于旧版本的更新，新部署的用户可以忽略此章节。</strong></p><p>为 ECharts 标签统计图和分类统计图增加点击跳转的功能，分别在 <code>postsChart</code>、<code>tagsChart</code>、<code>categoriesChart</code> 内部增加以下 js 即可实现页面跳转。</p><p><div class="tabs"><div class="nav-tabs"><button type="button" class="tab">V1.0</button><button type="button" class="tab active">V2.0</button></div><div class="tab-contents"><div class="tab-item-content"><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 归档页面跳转</span></span><br><span class="line">postsChart.on(<span class="string">&#x27;click&#x27;</span>, <span class="string">&#x27;series&#x27;</span>, <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (event.componentType === <span class="string">&#x27;series&#x27;</span> &amp;&amp; event.value) <span class="built_in">window</span>.location.href = <span class="string">&#x27;/archives/&#x27;</span> + event.name.replace(<span class="string">&#x27;-&#x27;</span>, <span class="string">&#x27;/&#x27;</span>) + <span class="string">&#x27;/&#x27;</span>;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 标签页面跳转</span></span><br><span class="line">tagsChart.on(<span class="string">&#x27;click&#x27;</span>, <span class="string">&#x27;series&#x27;</span>, <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (event.componentType === <span class="string">&#x27;series&#x27;</span>) <span class="built_in">window</span>.location.href = <span class="string">&#x27;/tags/&#x27;</span> + event.name + <span class="string">&#x27;/&#x27;</span>;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 分类页面跳转</span></span><br><span class="line">categoriesChart.on(<span class="string">&#x27;click&#x27;</span>, <span class="string">&#x27;series&#x27;</span>, <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (event.componentType === <span class="string">&#x27;series&#x27;</span>) <span class="built_in">window</span>.location.href = <span class="string">&#x27;/categories/&#x27;</span> + event.name + <span class="string">&#x27;/&#x27;</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p></div><div class="tab-item-content active"><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 归档页面跳转</span></span><br><span class="line">postsChart.on(<span class="string">&#x27;click&#x27;</span>, <span class="string">&#x27;series&#x27;</span>, <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (event.componentType === <span class="string">&#x27;series&#x27;</span> &amp;&amp; event.value) <span class="built_in">window</span>.location.href = <span class="string">&#x27;/archives/&#x27;</span> + event.name.replace(<span class="string">&#x27;-&#x27;</span>, <span class="string">&#x27;/&#x27;</span>) + <span class="string">&#x27;/&#x27;</span>;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 标签页面跳转</span></span><br><span class="line">tagsChart.on(<span class="string">&#x27;click&#x27;</span>, <span class="string">&#x27;series&#x27;</span>, <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span>(event.data.path) <span class="built_in">window</span>.location.href = <span class="string">&#x27;/&#x27;</span> + event.data.path;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 分类页面跳转</span></span><br><span class="line">categoriesChart.on(<span class="string">&#x27;click&#x27;</span>, <span class="string">&#x27;series&#x27;</span>, <span class="function">(<span class="params">event</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span>(event.data.path) <span class="built_in">window</span>.location.href = <span class="string">&#x27;/&#x27;</span> + event.data.path;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p></div></div><div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></p><h2 id="Hexo-三连"><a href="#Hexo-三连" class="headerlink" title="Hexo 三连"></a>Hexo 三连</h2><p>执行 Hexo 三连</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo g &amp;&amp; hexo s</span><br></pre></td></tr></table></figure></p><h2 id="可能遇到的问题"><a href="#可能遇到的问题" class="headerlink" title="可能遇到的问题"></a>可能遇到的问题</h2><ul><li><p>控制台报错 <code>Uncaught ReferenceError: require is not defined</code></p><p><strong>解决方案：</strong></p><p>不能直接在页面引用 <code>charts.js</code>，是放在主题文件夹 <code>[Blogroot]\themes\butterfly\scripts\helpers\</code> 里面，<code>hexo g</code> 的时候会在 <code>div</code> 后面追加 echarts 图的 js。页面不需要引入 <code>charts.js</code>。</p></li></ul><ul><li><p>控制台报错 <code>Uncaught ReferenceError: echarts is not defined</code>。</p><p><strong>解决方案：</strong></p><p>需要在统计图的前引入 <code>echarts.js</code> 文件，最好是在页面的头部引入。</p></li></ul><ul><li><p>控制台报错 <code>Cannot find module &#39;cheerio&#39;</code>。</p><p><strong>解决方案：</strong></p><p>安装 <code>cheerio</code>，控制台执行 <code>npm i cheerio --save</code>。</p></li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      
      <comments>https://blog.eurkon.com/post/1213ef82.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Hive 面试题解析</title>
      <link>https://blog.eurkon.com/post/62c9bbde.html</link>
      <guid>https://blog.eurkon.com/post/62c9bbde.html</guid>
      <pubDate>Wed, 31 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Hive-基础&quot;&gt;&lt;a href=&quot;#Hive-基础&quot; class=&quot;headerlink&quot; title=&quot;Hive 基础&quot;&gt;&lt;/a&gt;Hive 基础&lt;/h2&gt;&lt;h3 id=&quot;请谈一下-Hive-的特点，Hive-和-RDBMS-有什么异同？&quot;&gt;&lt;a href=&quot;#</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Hive-基础"><a href="#Hive-基础" class="headerlink" title="Hive 基础"></a>Hive 基础</h2><h3 id="请谈一下-Hive-的特点，Hive-和-RDBMS-有什么异同？"><a href="#请谈一下-Hive-的特点，Hive-和-RDBMS-有什么异同？" class="headerlink" title="请谈一下 Hive 的特点，Hive 和 RDBMS 有什么异同？"></a>请谈一下 Hive 的特点，Hive 和 RDBMS 有什么异同？</h3><p>Hive 是基于 Hadoop 的一个数据仓库工具，可以将结构化的数据文件映射为一张数据库表，并提供完整的 SQL 查询功能，可以将 SQL 语句转换为 MapReduce 任务进行运行。其优点是学习成本低，可以通过类 SQL 语句快速实现简单的 MapReduce 统计，不必开发专门的 MapReduce 应用，十分适合数据仓库的统计分析，但是 Hive 不支持实时查询。</p><p>Hive 与关系型数据库的区别：</p><div class="table-container"><table><thead><tr><th>比较项</th><th>SQL</th><th>HiveQL</th></tr></thead><tbody><tr><td>ANSI SQL</td><td>支持</td><td>不完全支持</td></tr><tr><td>更新</td><td>UPDATE\INSERT\DELETE</td><td>INSERT OVERWRITE\INTO TABLE</td></tr><tr><td>事务</td><td>支持</td><td>不支持</td></tr><tr><td>模式</td><td>写模式</td><td>读模式</td></tr><tr><td>数据保存</td><td>块设备、本地文件系统</td><td>HDFS</td></tr><tr><td>延时</td><td>低</td><td>高</td></tr><tr><td>多表插入</td><td>不支持</td><td>支持</td></tr><tr><td>子查询</td><td>完全支持</td><td>只能用在 From 子句中</td></tr><tr><td>视图</td><td>Updatable</td><td>Read-only</td></tr><tr><td>可扩展性</td><td>低</td><td>高</td></tr><tr><td>数据规模</td><td>小</td><td>大</td></tr><tr><td>...</td><td>......</td><td>......</td></tr></tbody></table></div><h3 id="Hive-的架构原理？"><a href="#Hive-的架构原理？" class="headerlink" title="Hive 的架构原理？"></a>Hive 的架构原理？</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hive_architecture.png" alt="Hive 架构原理"></p><ol><li><p>两种客户端</p><ul><li>CLI（command-line interface）：命令行客户端（可以在 shell 中操作）；</li><li>JDBC 客户端</li></ul></li><li><p>四种驱动 Driver</p><ul><li>SQL Parser 解析器：检查 SQL 语法是否有错误；</li><li>Physical Plan 编译器：把 SQL 语句转化成 MR 程序；</li><li>Query Optimizer 优化器：优化 SQL 语句；</li><li>Execution 执行器：执行 MR 程序。</li></ul></li><li><p>元数据库 Meta store</p><ul><li>Meta store 是 Hive 数据库中的一个库，用于存储处理数据的元数据，包括：表名、表所属的数据库（默认是 default）、表的拥有者、列/分区字段、表的类型（是否是外部表）、表的数据所在目录等。</li><li>Meta store 默认存储在 Hive 自带的 Derby 数据库中，但因为 Hive 的元数据可能要面临不断地更新、修改和读取操作，Derby 无法实现并发，实际工作中一般将 Derby 替换为 MySQL。</li><li>Hive 中处理的数据分两部分存放：<ul><li>处理的结构化数据，存储在 HDFS 中；</li><li>表的元数据存储在元数据库 MySQL 中。</li></ul></li></ul></li></ol><p>这样，有了表的元数据信息，就能找到对应的字段；然后把字段映射到结构化数据中，这样就可以通过映射形成一张虚表；</p><p>也就是说，表的元数据和 MySQL 中的结构化数据，通过映射，构成一张虚表，用于 Hive 查询数据分析。</p><h3 id="Hive-的存储过程："><a href="#Hive-的存储过程：" class="headerlink" title="Hive 的存储过程："></a>Hive 的存储过程：</h3><p>启动 Hive 时，会初始化 Hive，这时会在 MySQL 中生成大约 36 张表（后续随着业务的复杂会增加），然后创建表，会在 MySQL 中存放这个表的信息（不是以表的形式存在的，而是把表的属性以数据的形式放在 MySQL 中，这样在 Hive 中使用 SQL 命令一样是能够查到这张表的）。然后把本地的文本文件使用 Hive 命令格式化导入到表中，这样这些数据就存放到 HDFS 中，而不是在 MySQL 或 Hive 中。</p><h3 id="Hive-有哪些方式保存元数据，各有哪些特点？"><a href="#Hive-有哪些方式保存元数据，各有哪些特点？" class="headerlink" title="Hive 有哪些方式保存元数据，各有哪些特点？"></a>Hive 有哪些方式保存元数据，各有哪些特点？</h3><p>Hive 支持三种不同的元存储服务器，分别为：内嵌式元存储服务器、本地元存储服务器、远程元存储服务器，每种存储方式使用不同的配置参数。</p><ul><li>内嵌式元存储主要用于单元测试，在该模式下每次只有一个进程可以连接到元存储，Derby 是内嵌式元存储的默认数据库。</li><li>在本地模式下，每个 Hive 客户端都会打开到数据存储的连接并在该连接上请求 SQL 查询。</li><li>在远程模式下，所有的 Hive 客户端都将打开一个到元数据服务器的连接，该服务器依次查询元数据，元数据服务器和客户端之间使用 Thrift 协议通信。</li></ul><h3 id="Hive-内部表和外部表的区别？"><a href="#Hive-内部表和外部表的区别？" class="headerlink" title="Hive 内部表和外部表的区别？"></a>Hive 内部表和外部表的区别？</h3><ul><li>创建表时：创建内部表时，会将数据移动到数据仓库指向的路径；若创建外部表，仅记录数据所在的路径，不对数据的位置做任何改变。</li><li>删除表时：在删除表的时候，内部表的元数据和数据会被一起删除，而外部表只删除元数据，不删除数据。这样外部表相对来说更加安全些，数据组织也更加灵活，方便共享源数据。</li></ul><h3 id="Hive-中的压缩格式-TextFile、SequenceFile、RCFile-、ORCFile-各有什么区别？"><a href="#Hive-中的压缩格式-TextFile、SequenceFile、RCFile-、ORCFile-各有什么区别？" class="headerlink" title="Hive 中的压缩格式 TextFile、SequenceFile、RCFile 、ORCFile 各有什么区别？"></a>Hive 中的压缩格式 TextFile、SequenceFile、RCFile 、ORCFile 各有什么区别？</h3><ul><li><p>TextFile</p><p>默认格式，存储方式为行存储，数据不做压缩，磁盘开销大，数据解析开销大。可结合 Gzip、Bzip2 使用（系统自动检查，执行查询时自动解压），但使用这种方式，压缩后的文件不支持 split，Hive 不会对数据进行切分，从而无法对数据进行并行操作。并且在反序列化过程中，必须逐个字符判断是不是分隔符和行结束符，因此反序列化开销会比 SequenceFile 高几十倍。</p></li><li><p>SequenceFile</p><p>SequenceFile 是 Hadoop API 提供的一种二进制文件支持，存储方式为行存储，其具有使用方便、可分割、可压缩的特点。</p><p>SequenceFile 支持三种压缩选择：NONE，RECORD，BLOCK。Record 压缩率低，一般建议使用 BLOCK 压缩。</p><p>优势是文件和 Hadoop API 中的 MapFile 是相互兼容的。</p></li><li><p>RCFile</p><p>存储方式：数据按行分块，每块按列存储。结合了行存储和列存储的优点：</p><p>首先，RCFile 保证同一行的数据位于同一节点，因此元组重构的开销很低；</p><p>其次，像列存储一样，RCFile 能够利用列维度的数据压缩，并且能跳过不必要的列读取；</p></li><li><p>ORCFile</p><p>存储方式：数据按行分块 每块按照列存储。</p><p>压缩快、快速列存取。</p><p>效率比 RCFile 高，是 RCFile 的改良版本。</p></li></ul><p><strong>总结：</strong>相比 TextFile 和 SequenceFile，RCFile 由于列式存储方式，数据加载时性能消耗较大，但是具有较好的压缩比和查询响应。数据仓库的特点是一次写入、多次读取，因此，整体来看，RCFile 相比其余两种格式具有较明显的优势。</p><h3 id="说说对-Hive-桶表的理解？"><a href="#说说对-Hive-桶表的理解？" class="headerlink" title="说说对 Hive 桶表的理解？"></a>说说对 Hive 桶表的理解？</h3><p>桶表是对数据进行哈希取值，然后放到不同文件中存储。</p><p>数据加载到桶表时，会对字段取 hash 值，然后与桶的数量取模。把数据放到对应的文件中。物理上，每个桶就是表（或分区）目录里的一个文件，一个作业产生的桶（输出文件）和 reduce 任务个数相同。</p><p>桶表专门用于抽样查询，是很专业性的，不是日常用来存储数据的表，需要抽样查询时，才创建和使用桶表。</p><h3 id="Hive-的-HiveSQL-转换为-MapReduce-的过程？"><a href="#Hive-的-HiveSQL-转换为-MapReduce-的过程？" class="headerlink" title="Hive 的 HiveSQL 转换为 MapReduce 的过程？"></a>Hive 的 HiveSQL 转换为 MapReduce 的过程？</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hive_sqlparsemr.png" alt="HiveSQL 转换为 MapReduce 的过程"></p><ol><li>SQL Parser：Antlr 定义 SQL 的语法规则，完成 SQL 词法，语法解析，将 SQL 转化为抽象语法树 AST Tree；</li><li>Semantic Analyzer：遍历 AST Tree，抽象出查询的基本组成单元 QueryBlock；</li><li>Logical plan：遍历 QueryBlock，翻译为执行操作树 OperatorTree；</li><li>Logical plan optimizer: 逻辑层优化器进行 OperatorTree 变换，合并不必要的 ReduceSinkOperator，减少 shuffle 数据量；</li><li>Physical plan：遍历 OperatorTree，翻译为 MapReduce 任务；</li><li>Logical plan optimizer：物理层优化器进行 MapReduce 任务的变换，生成最终的执行计划。</li></ol><h3 id="所有的-Hive-任务都会有-MapReduce-的执行吗？"><a href="#所有的-Hive-任务都会有-MapReduce-的执行吗？" class="headerlink" title="所有的 Hive 任务都会有 MapReduce 的执行吗？"></a>所有的 Hive 任务都会有 MapReduce 的执行吗？</h3><p>不是，从 Hive0.10.0 版本开始，对于简单的不需要聚合的类似 <code>SELECT * FROM LIMIT n</code> 语句，不需要起 MapReduce job，直接通过 Fetch task 获取数据。</p><h3 id="什么是-Fetch-抓取"><a href="#什么是-Fetch-抓取" class="headerlink" title="什么是 Fetch 抓取"></a>什么是 Fetch 抓取</h3><p>Fetch 抓取是指，Hive 中对某些情况的查询可以不必使用 MapReduce 计算。例如：<code>SELECT * FROM employees</code>，在这种情况下，Hive 可以简单地读取 employee 对应的存储目录下的文件，然后输出查询结果到控制台。</p><p>在 <code>hive-default.xml.template</code> 文件中 <code>hive.fetch.task.conversion</code> 默认是 <code>more</code>，老版本 Hive 默认是 <code>minimal</code>，该属性修改为 <code>more</code> 以后，在全局查找、字段查找、limit 查找等都不走 MapReduce。</p><h3 id="什么是本地模式"><a href="#什么是本地模式" class="headerlink" title="什么是本地模式"></a>什么是本地模式</h3><p>大多数的 Hadoop Job 是需要 Hadoop 提供的完整的可扩展性来处理大数据集的。不过，有时 Hive 的输入数据量是非常小的。在这种情况下，为查询触发执行任务时消耗可能会比实际 job 的执行时间要多的多。对于大多数这种情况，Hive 可以通过本地模式在单台机器上处理所有的任务。对于小数据集，执行时间可以明显被缩短。</p><p>用户可以通过设置 <code>hive.exec.mode.local.auto</code> 的值为 <code>true</code>，来让 Hive 在适当的时候自动启动这个优化。</p><h3 id="Hive-的函数：UDF、UDAF、UDTF-的区别？"><a href="#Hive-的函数：UDF、UDAF、UDTF-的区别？" class="headerlink" title="Hive 的函数：UDF、UDAF、UDTF 的区别？"></a>Hive 的函数：UDF、UDAF、UDTF 的区别？</h3><ul><li>UDF：User-Defined Function，用户定义（普通）函数，只对单行数值产生作用；单行进入，单行输出。</li><li>UDAF：User-Defined Aggregation Function，用户定义聚合函数，可对多行数据产生作用；等同与 SQL 中常用的 <code>SUM()</code>，<code>AVG()</code>，也是聚合函数；多行进入，单行输出。</li><li>UDTF：User-Defined Table-Generating Functions，用户定义表生成函数，用来解决输入一行输出多行，如 <code>lateral view explore()</code>；单行输入，多行输出。</li></ul><h3 id="请说明-Hive-中-Order-By，Sort-By，Distribute-By，Cluster-By-各代表什么意思？"><a href="#请说明-Hive-中-Order-By，Sort-By，Distribute-By，Cluster-By-各代表什么意思？" class="headerlink" title="请说明 Hive 中 Order By，Sort By，Distribute By，Cluster By 各代表什么意思？"></a>请说明 Hive 中 Order By，Sort By，Distribute By，Cluster By 各代表什么意思？</h3><ul><li>order by：会对输入做全局排序，因此只有一个 reducer（多个 reducer 无法保证全局有序）。只有一个 reducer，会导致当输入规模较大时，需要较长的计算时间。</li><li>sort by：不是全局排序，其在数据进入 reducer 前完成排序。</li><li>distribute by：按照指定的字段对数据进行划分输出到不同的 reduce 中。</li><li>cluster by：除了具有 distribute by 的功能外还兼具 sort by 的功能。</li></ul><h3 id="写出-Hive-中-split、coalesce-及-collect-list-函数的用法（可举例）？"><a href="#写出-Hive-中-split、coalesce-及-collect-list-函数的用法（可举例）？" class="headerlink" title="写出 Hive 中 split、coalesce 及 collect_list 函数的用法（可举例）？"></a>写出 Hive 中 split、coalesce 及 collect_list 函数的用法（可举例）？</h3><ul><li><code>split</code> 将字符串转化为数组，即：<code>split(&#39;a,b,c,d&#39;, &#39;,&#39;) ==&gt; [&quot;a&quot;,&quot;b&quot;,&quot;c&quot;,&quot;d&quot;]</code>。</li><li><code>coalesce(T v1, T v2, …)</code> 返回参数中的第一个非空值；如果所有值都为 <code>NULL</code>，那么返回 <code>NULL</code>。</li><li><code>collect_list</code> 列出该字段所有的值，不去重 =&gt; <code>select collect_list(id) from table</code>。</li></ul><h3 id="Hive-的两张表关联，使用-MapReduce-怎么实现？"><a href="#Hive-的两张表关联，使用-MapReduce-怎么实现？" class="headerlink" title="Hive 的两张表关联，使用 MapReduce 怎么实现？"></a>Hive 的两张表关联，使用 MapReduce 怎么实现？</h3><ul><li><p>如果其中有一张表为小表，直接使用 map 端 join 的方式（map 端加载小表）进行聚合。</p></li><li><p>如果两张都是大表，那么采用联合 key，联合 key 的第一个组成部分是 join on 中的公共字段，第二部分是一个 flag，0 代表表 A，1 代表表 B，由此让 Reduce 区分客户信息和订单信息；在 Mapper 中同时处理两张表的信息，将 join on 公共字段相同的数据划分到同一个分区中，进而传递到一个 Reduce 中，然后在 Reduce 中实现聚合。</p></li></ul><h2 id="Hive-优化"><a href="#Hive-优化" class="headerlink" title="Hive 优化"></a>Hive 优化</h2><h3 id="小表、大表-JOIN-优化"><a href="#小表、大表-JOIN-优化" class="headerlink" title="小表、大表 JOIN 优化"></a>小表、大表 JOIN 优化</h3><p>将 key 相对分散，并且数据量小的表放在 JOIN 的左边，这样可以有效减少内存溢出错误发生的几率；再进一步，可以使用 Group 让小的维度表（1000 条以下的记录条数）先进内存。在 map 端完成 reduce。</p><p>实际测试发现：新版的 Hive 已经对小表 JOIN 大表和大表 JOIN 小表进行了优化。小表放在左边和右边已经没有明显区别。</p><h3 id="大表-JOIN-大表优化"><a href="#大表-JOIN-大表优化" class="headerlink" title="大表 JOIN 大表优化"></a>大表 JOIN 大表优化</h3><ul><li><p>空 KEY 过滤</p><p>有时 JOIN 超时是因为某些 key 对应的数据太多，而相同 key 对应的数据都会发送到相同的 reducer 上，从而导致内存不够。此时我们应该仔细分析这些异常的 key，很多情况下，这些 key 对应的数据是异常数据，我们需要在 SQL 语句中进行过滤。例如 key 对应的字段为空。</p></li><li><p>空 key 转换</p><p>有时虽然某个 key 为空对应的数据很多，但是相应的数据不是异常数据，必须要包含在 JOIN 的结果中，此时我们可以表 A 中 key 为空的字段赋一个随机的值，使得数据随机均匀地分不到不同的 reducer 上。</p></li></ul><h3 id="Group-By-优化"><a href="#Group-By-优化" class="headerlink" title="Group By 优化"></a>Group By 优化</h3><p>默认情况下，Map 阶段同一 Key 数据分发给一个 reduce，当一个 key 数据过大时就倾斜了。</p><p>并不是所有的聚合操作都需要在 Reduce 端完成，很多聚合操作都可以先在 Map 端进行部分聚合，最后在 Reduce 端得出最终结果。</p><p>开启 Map 端聚合参数设置</p><ul><li>是否在 Map 端进行聚合，默认为 true：<code>hive.map.aggr = true</code></li><li>在 Map 端进行聚合操作的条目数目：<code>hive.groupby.mapaggr.checkinterval = 100000</code></li><li>有数据倾斜的时候进行负载均衡（默认是 false）：<code>hive.groupby.skewindata = true</code></li></ul><p>当选项设定为 true，生成的查询计划会有两个 MR Job。第一个 MR Job 中，Map 的输出结果会随机分布到 Reduce 中，每个 Reduce 做部分聚合操作，并输出结果，这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中，从而达到负载均衡的目的；第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中（这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中），最后完成最终的聚合操作。</p><h3 id="替换-COUNT-Distinct-去重统计"><a href="#替换-COUNT-Distinct-去重统计" class="headerlink" title="替换 COUNT(Distinct) 去重统计"></a>替换 COUNT(Distinct) 去重统计</h3><p>数据量小的时候无所谓，数据量大的情况下，由于 COUNT DISTINCT 操作需要用一个 Reduce Task 来完成，这一个 Reduce 需要处理的数据量太大，就会导致整个 Job 很难完成，一般 COUNT DISTINCT 使用先 GROUP BY 再 COUNT 的方式替换。</p><h3 id="避免笛卡尔积"><a href="#避免笛卡尔积" class="headerlink" title="避免笛卡尔积"></a>避免笛卡尔积</h3><p>尽量避免笛卡尔积，join 的时候不加 on 条件，或者无效的 on 条件，Hive 只能使用 1 个 reducer 来完成笛卡尔积。</p><h3 id="行列过滤"><a href="#行列过滤" class="headerlink" title="行列过滤"></a>行列过滤</h3><ul><li>列处理：在 SELECT 中，只拿需要的列，如果有，尽量使用分区过滤，少用 <code>SELECT *</code>。</li><li>行处理：在分区剪裁中，当使用外关联时，如果将副表的过滤条件写在 where 后面，那么就会先全表关联，之后再过滤。</li></ul><h3 id="Hive-表关联查询，如何解决数据倾斜的问题？"><a href="#Hive-表关联查询，如何解决数据倾斜的问题？" class="headerlink" title="Hive 表关联查询，如何解决数据倾斜的问题？"></a>Hive 表关联查询，如何解决数据倾斜的问题？</h3><ul><li><p>倾斜原因：map 输出数据按 key Hash 的分配到 reduce 中，由于 key 分布不均匀、业务数据本身的特点、建表时考虑不周等原因造成的 reduce 上的数据量差异过大。</p><ul><li>key 分布不均匀;</li><li>业务数据本身的特性;</li><li>建表时考虑不周;</li><li><p>某些 SQL 语句本身就有数据倾斜;</p><p>如何避免：对于 key 为空产生的数据倾斜，可以对其赋予一个随机值。</p></li></ul></li><li><p>解决方案</p><ul><li><p>参数调节：</p><p><code>hive.map.aggr=true</code></p><p><code>hive.groupby.skewindata=true</code></p><p>有数据倾斜的时候进行负载均衡，当选项设定位 true，生成的查询计划会有两个 MR Job。</p><p>第一个 MR Job 中，Map 的输出结果集合会随机分布到 Reduce 中，每个 Reduce 做部分聚合操作，并输出结果，这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中，从而达到负载均衡的目的；</p><p>第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中（这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中），最后完成最终的聚合操作。</p></li><li><p>SQL 语句调节：</p><ul><li>选用 join key 分布最均匀的表作为驱动表。做好列裁剪和 filter 操作，以达到两表做 join 的时候，数据量相对变小的效果。</li><li>大小表 join：使用 map join 让小的维度表（1000 条以下的记录条数）先进内存。在 map 端完成 reduce。</li><li>大表 join 大表：把空值的 key 变成一个字符串加上随机数，把倾斜的数据分到不同的 reduce 上，由于 null 值关联不上，处理后并不影响最终结果。</li><li>count distinct 大量相同特殊值：count distinct 时，将值为空的情况单独处理，如果是计算 count distinct，可以不用处理，直接过滤，在最后结果中加 1。如果还有其他计算，需要进行 group by，可以先将值为空的记录单独处理，再和其他计算结果进行 union。</li></ul></li></ul></li></ul><h3 id="优化-Map-数"><a href="#优化-Map-数" class="headerlink" title="优化 Map 数"></a>优化 Map 数</h3><ol><li><p>通常情况下，作业会通过 input 的目录产生一个或者多个 map 任务。</p><p>主要的决定因素有：input 的文件总个数，input 的文件大小，集群设置的文件块大小。</p></li><li><p>是不是 map 数越多越好？</p><p>不一定。如果一个任务有很多小文件（远远小于块大小 128MB），则每个小文件也会被当做一个块，用一个 map 任务来完成，而一个 map 任务启动和初始化的时间远远大于逻辑处理的时间，就会造成很大的资源浪费。而且，同时可执行的 map 数是受限的。</p></li><li><p>是不是保证每个 map 处理接近 128MB 的文件块，就高枕无忧了？</p><p>答案也是不一定。比如有一个 127MB 的文件，正常会用一个 map 去完成，但这个文件只有一个或者两个小字段，却有几千万的记录，如果 map 处理的逻辑比较复杂，用一个 map 任务去做，肯定也比较耗时。</p></li></ol><p><strong>注意：</strong>针对上面的问题 2 和 3，我们需要采取两种方式来解决：即减少 map 数和增加 map 数。</p><h3 id="小文件进行合并"><a href="#小文件进行合并" class="headerlink" title="小文件进行合并"></a>小文件进行合并</h3><p>在 map 执行前合并小文件，减少 map 数：CombineHiveInputFormat 具有对小文件进行合并的功能（系统默认的格式）。HiveInputFormat 没有对小文件合并功能。</p><p><code>set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat</code></p><h3 id="复杂文件增加-Map-数"><a href="#复杂文件增加-Map-数" class="headerlink" title="复杂文件增加 Map 数"></a>复杂文件增加 Map 数</h3><p>当 input 的文件都很大，任务逻辑复杂，map 执行非常慢的时候，可以考虑增加 Map 数，来使得每个 map 处理的数据量减少，从而提高任务的执行效率。</p><p>增加 map 的方法为：根据 <code>computeSliteSize(Math.max(minSize, Math.min(maxSize, blocksize))) = blocksize = 128MB</code> 公式，调整 maxSize 最大值。让 maxSize 最大值低于 blocksize 就可以增加 map 的个数。</p><h3 id="优化-Reduce-数"><a href="#优化-Reduce-数" class="headerlink" title="优化 Reduce 数"></a>优化 Reduce 数</h3><ol><li><p>调整 reduce 个数方法一</p><ul><li><p>每个 Reduce 处理的数据量默认是 256MB</p><p><code>hive.exec.reducers.bytes.per.reducer=256000000</code></p></li><li><p>每个任务最大的 reduce 数，默认为 1009</p><p><code>hive.exec.reducers.max=1009</code></p></li><li><p>计算 reducer 数的公式</p><p>N = min(参数 2, 总输入数据量/参数 1)</p></li></ul></li><li><p>调整 reduce 个数方法二</p><p> 在 hadoop 的 <code>mapred-default.xml</code> 文件中修改设置每个 job 的 Reduce 个数</p><p> <code>set mapreduce.job.reduces = 15</code></p></li><li><p>reduce 个数并不是越多越好</p><ul><li>过多的启动和初始化 reduce 也会消耗时间和资源；</li><li><p>另外，有多少个 reduce，就会有多少个输出文件，如果生成了很多个小文件，那么如果这些小文件作为下一个任务的输入，则也会出现小文件过多的问题；</p><p>在设置 reduce 个数的时候也需要考虑这两个原则：处理大数据量利用合适的 reduce 数；使单个 reduce 任务处理数据量大小要合适。</p></li></ul></li></ol><h3 id="并行执行"><a href="#并行执行" class="headerlink" title="并行执行"></a>并行执行</h3><p>Hive 会将一个查询转化成一个或者多个阶段。这样的阶段可以是 MapReduce 阶段、抽样阶段、合并阶段、limit 阶段。或者 Hive 执行过程中可能需要的其他阶段。默认情况下，Hive 一次只会执行一个阶段。不过，某个特定的 job 可能包含众多的阶段，而这些阶段可能并非完全互相依赖的，也就是说有些阶段是可以并行执行的，这样可能使得整个 job 的执行时间缩短。不过，如果有更多的阶段可以并行执行，那么 job 可能就越快完成。</p><p>通过设置参数 <code>hive.exec.parallel=true</code>，就可以开启并发执行。不过，在共享集群中，需要注意下，如果 job 中并行阶段增多，那么集群利用率就会增加。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Hive/">Hive</category>
      
      
      <comments>https://blog.eurkon.com/post/62c9bbde.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Kafka 面试题解析</title>
      <link>https://blog.eurkon.com/post/89cabcb1.html</link>
      <guid>https://blog.eurkon.com/post/89cabcb1.html</guid>
      <pubDate>Tue, 30 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;请说明什么是-Apache-Kafka？&quot;&gt;&lt;a href=&quot;#请说明什么是-Apache-Kafka？&quot; class=&quot;headerlink&quot; title=&quot;请说明什么是 Apache Kafka？&quot;&gt;&lt;/a&gt;请说明什么是 Apache Kafka？&lt;/h2&gt;&lt;</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="请说明什么是-Apache-Kafka？"><a href="#请说明什么是-Apache-Kafka？" class="headerlink" title="请说明什么是 Apache Kafka？"></a>请说明什么是 Apache Kafka？</h2><p>Apache Kafka 是由 Apache 开发的一种发布订阅消息系统，它是一个分布式的、分区的和重复的日志服务。</p><h2 id="请说明什么是传统的消息传递方法？"><a href="#请说明什么是传统的消息传递方法？" class="headerlink" title="请说明什么是传统的消息传递方法？"></a>请说明什么是传统的消息传递方法？</h2><p>传统的消息传递方法包括两种：</p><ul><li>队列：在队列中，一组用户可以从服务器中读取消息，每条消息都发送给其中一个人。</li><li>发布-订阅：在这个模型中，消息被广播给所有的用户。</li></ul><h2 id="Kafka-都有哪些特点？"><a href="#Kafka-都有哪些特点？" class="headerlink" title="Kafka 都有哪些特点？"></a>Kafka 都有哪些特点？</h2><ul><li>高吞吐量、低延迟：Kafka 每秒可以处理几十万条消息，它的延迟最低只有几毫秒，每个 topic 可以分多个 partition、consumer group 对 partition 进行 consume 操作。</li><li>可扩展性：Kafka 集群支持热扩展。</li><li>持久性、可靠性：消息被持久化到本地磁盘，并且支持数据备份防止数据丢失。</li><li>容错性：允许集群中节点失败（若副本数量为 n，则允许 n-1 个节点失败）。</li><li>高并发：支持数千个客户端同时读写。</li></ul><h2 id="请说明-Kafka-相对于传统的消息传递方法有什么优势？"><a href="#请说明-Kafka-相对于传统的消息传递方法有什么优势？" class="headerlink" title="请说明 Kafka 相对于传统的消息传递方法有什么优势？"></a>请说明 Kafka 相对于传统的消息传递方法有什么优势？</h2><ul><li>高性能：单一的 Kafka 代理可以处理成千上万的客户端，每秒处理数兆字节的读写操作，Kafka 性能远超过传统的 ActiveMQ、RabbitMQ 等，而且 Kafka 支持 Batch 操作；</li><li>可扩展：Kafka 集群可以透明的扩展，增加新的服务器进集群；</li><li>容错性：Kafka 每个 Partition 数据会复制到几台服务器，当某个 Broker 失效时，Zookeeper 将通知生产者和消费者从而使用其他的 Broker。</li></ul><h2 id="请简述下你在哪些场景下会选择-Kafka？"><a href="#请简述下你在哪些场景下会选择-Kafka？" class="headerlink" title="请简述下你在哪些场景下会选择 Kafka？"></a>请简述下你在哪些场景下会选择 Kafka？</h2><ul><li>日志收集：一个公司可以用 Kafka 收集各种服务的 log，通过 Kafka 以统一接口服务的方式开放给各种 consumer，例如 Hadoop、HBase、Solr 等。</li><li>消息系统：解耦和生产者和消费者、缓存消息等。</li><li>用户活动跟踪：Kafka 经常被用来记录 web 用户或者 app 用户的各种活动，如浏览网页、搜索、点击等活动，这些活动信息被各个服务器发布到 Kafka 的 topic 中，然后订阅者通过订阅这些 topic 来做实时的监控分析，或者装载到 Hadoop、数据仓库中做离线分析和挖掘。</li><li>运营指标：Kafka 也经常用来记录运营监控数据，包括收集各种分布式应用的数据，生产各种操作的集中反馈，比如报警和报告。</li><li>流式处理：比如 Spark streaming 和 Flink。</li></ul><h2 id="Kafka-服务器能接收到的最大信息是多少？"><a href="#Kafka-服务器能接收到的最大信息是多少？" class="headerlink" title="Kafka 服务器能接收到的最大信息是多少？"></a>Kafka 服务器能接收到的最大信息是多少？</h2><p>Kafka 服务器可以接收到的消息的最大大小是 1000 000 字节。</p><h2 id="Kafka-中的-ZooKeeper-是什么？Kafka-是否可以脱离-ZooKeeper-独立运行？"><a href="#Kafka-中的-ZooKeeper-是什么？Kafka-是否可以脱离-ZooKeeper-独立运行？" class="headerlink" title="Kafka 中的 ZooKeeper 是什么？Kafka 是否可以脱离 ZooKeeper 独立运行？"></a>Kafka 中的 ZooKeeper 是什么？Kafka 是否可以脱离 ZooKeeper 独立运行？</h2><p>Zookeeper 是一个开放源码的、高性能的协调服务，它用于 Kafka 的分布式应用。</p><p>不可以，不可能越过 Zookeeper 直接联系 Kafka broker，一旦 Zookeeper 停止工作，它就不能服务客户端请求。</p><p>Zookeeper 主要用于在集群中不同节点之间进行通信，在 Kafka 中，它被用于提交偏移量，因此如果节点在任何情况下都失败了，它都可以从之前提交的偏移量中获取，除此之外，它还执行其他活动，如: <strong>Leader 检测</strong>、<strong>分布式同步</strong>、<strong>配置管理</strong>、<strong>识别新节点何时离开或连接</strong>、<strong>集群</strong>、<strong>节点实时状态</strong>等等。</p><h2 id="Kafka-的设计架构？"><a href="#Kafka-的设计架构？" class="headerlink" title="Kafka 的设计架构？"></a>Kafka 的设计架构？</h2><ul><li>简单架构</li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/kafka_simple.png" alt="Kafka 简单架构"></p><ul><li>详细架构如下：</li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/kafka_architecture.png" alt="Kafka 详细架构"></p><p>Kafka 架构分为以下几个部分：</p><ul><li>Producer：消息生产者，就是向 Kafka broker 发消息的客户端。</li><li>Consumer：消息消费者，向 Kafka broker 取消息的客户端。</li><li>Topic：可以理解为一个队列，一个 Topic 又分为一个或多个分区。</li><li>Consumer Group：这是 Kafka 用来实现一个 topic 消息的广播（发给所有的 consumer）和单播（发给任意一个 consumer）的手段。一个 topic 可以有多个 Consumer Group。</li><li>Broker：一台 Kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic。</li><li>Partition：为了实现扩展性，一个非常大的 topic 可以分布到多个 broker 上，每个 partition 是一个有序的队列。partition 中的每条消息都会被分配一个有序的 id（offset）。将消息发给 consumer，Kafka 只保证按一个 partition 中的消息的顺序，不保证一个 topic 的整体（多个 partition 间）的顺序。</li><li>Offset：Kafka 的存储文件都是按照 <code>offset.kafka</code> 来命名，用 offset 做名字的好处是方便查找。例如你想找位于 2049 的位置，只要找到 <code>2048.kafka</code> 的文件即可。当然 the first offset 就是 <code>00000000000.kafka</code>。</li></ul><h2 id="解释-Kafka-的用户如何消费信息？"><a href="#解释-Kafka-的用户如何消费信息？" class="headerlink" title="解释 Kafka 的用户如何消费信息？"></a>解释 Kafka 的用户如何消费信息？</h2><p>在 Kafka 中传递消息是通过使用 sendfile API 完成的。它支持将字节 Socket 转移到磁盘，通过内核空间保存副本，并在内核用户之间调用内核。</p><h2 id="解释如何提高远程用户的吞吐量？"><a href="#解释如何提高远程用户的吞吐量？" class="headerlink" title="解释如何提高远程用户的吞吐量？"></a>解释如何提高远程用户的吞吐量？</h2><p>如果用户位于与 broker 不同的数据中心，则可能需要调优 Socket 缓冲区大小，以对长网络延迟进行摊销。</p><h2 id="解释一下，在数据制作过程中，你如何能从-Kafka-得到准确的信息？"><a href="#解释一下，在数据制作过程中，你如何能从-Kafka-得到准确的信息？" class="headerlink" title="解释一下，在数据制作过程中，你如何能从 Kafka 得到准确的信息？"></a>解释一下，在数据制作过程中，你如何能从 Kafka 得到准确的信息？</h2><p>在数据中，为了精确地获得 Kafka 的消息，你必须遵循两件事：在数据消耗期间避免重复，在数据生产过程中避免重复。</p><p>这里有两种方法，可以在数据生成时准确地获得一个语义:</p><ul><li>每个分区使用一个单独的写入器，每当你发现一个网络错误，检查该分区中的最后一条消息，以查看您的最后一次写入是否成功。</li><li>在消息中包含一个主键(UUID 或其他)，并在用户中进行反复制。</li></ul><h2 id="Kafka-为什么需要复制？"><a href="#Kafka-为什么需要复制？" class="headerlink" title="Kafka 为什么需要复制？"></a>Kafka 为什么需要复制？</h2><p>Kafka 的信息复制确保了任何已发布的消息不会丢失，并且可以在机器错误、程序错误或更常见些的软件升级中使用。</p><h2 id="Kafka-分区的目的？"><a href="#Kafka-分区的目的？" class="headerlink" title="Kafka 分区的目的？"></a>Kafka 分区的目的？</h2><p>分区对于 Kafka 集群的好处是：实现负载均衡。分区对于消费者来说，可以提高并发度，提高效率。</p><h2 id="Kafka-是如何做到消息的有序性？"><a href="#Kafka-是如何做到消息的有序性？" class="headerlink" title="Kafka 是如何做到消息的有序性？"></a>Kafka 是如何做到消息的有序性？</h2><p>kafka 中的每个 partition 中的消息在写入时都是有序的，而且单独一个 partition 只能由一个消费者去消费，可以在里面保证消息的顺序性。但是分区之间的消息是不保证有序的。</p><h2 id="Kafka-的高可靠性是怎么实现的？"><a href="#Kafka-的高可靠性是怎么实现的？" class="headerlink" title="Kafka 的高可靠性是怎么实现的？"></a>Kafka 的高可靠性是怎么实现的？</h2><p>可回答：Kafka 在什么情况下会出现消息丢失？</p><blockquote><p><strong>数据可靠性（可回答 怎么尽可能保证 Kafka 的可靠性？）</strong></p></blockquote><p>Kafka 作为一个商业级消息中间件，消息可靠性的重要性可想而知。本文从 Producer 往 Broker 发送消息、Topic 分区副本以及 Leader 选举几个角度介绍数据的可靠性。</p><ul><li><p>Topic 分区副本</p><p>在 Kafka 0.8.0 之前，Kafka 是没有副本的概念的，那时候人们只会用 Kafka 存储一些不重要的数据，因为没有副本，数据很可能会丢失。但是随着业务的发展，支持副本的功能越来越强烈，所以为了保证数据的可靠性，Kafka 从 0.8.0 版本开始引入了分区副本（详情请参见 KAFKA-50）。也就是说每个分区可以人为的配置几个副本（比如创建主题的时候指定 <code>replication-factor</code>，也可以在 Broker 级别进行配置 <code>default.replication.factor</code>），一般会设置为 3。</p><p>Kafka 可以保证单个分区里的事件是有序的，分区可以在线（可用），也可以离线（不可用）。在众多的分区副本里面有一个副本是 Leader，其余的副本是 Follower，所有的读写操作都是经过 Leader 进行的，同时 Follower 会定期地去 Leader 上的复制数据。当 Leader 挂了的时候，其中一个 Follower 会重新成为新的 Leader。通过分区副本，引入了数据冗余，同时也提供了 Kafka 的数据可靠性。</p><p>Kafka 的分区多副本架构是 Kafka 可靠性保证的核心，把消息写入多个副本可以使 Kafka 在发生崩溃时仍能保证消息的持久性。</p></li><li><p>Producer 往 Broker 发送消息</p><p>如果我们要往 Kafka 对应的主题发送消息，我们需要通过 Producer 完成。前面我们讲过 Kafka 主题对应了多个分区，每个分区下面又对应了多个副本；为了让用户设置数据可靠性，Kafka 在 Producer 里面提供了消息确认机制。也就是说我们可以通过配置来决定消息发送到对应分区的几个副本才算消息发送成功。可以在定义 Producer 时通过 <code>acks</code> 参数指定（在 0.8.2.X 版本之前是通过 <code>request.required.acks</code> 参数设置的）。</p><p>这个参数支持以下三种值：</p><ul><li><p><code>acks = 0</code>：意味着如果生产者能够通过网络把消息发送出去，那么就认为消息已成功写入 Kafka。在这种情况下还是有可能发生错误，比如发送的对象无能被序列化或者网卡发生故障，但如果是分区离线或整个集群长时间不可用，那就不会收到任何错误。在 <code>acks = 0</code> 模式下的运行速度是非常快的（这就是为什么很多基准测试都是基于这个模式），你可以得到惊人的吞吐量和带宽利用率，不过如果选择了这种模式，一定会丢失一些消息。</p></li><li><p><code>acks = 1</code>：意味若 Leader 在收到消息并把它写入到分区数据文件（不一定同步到磁盘上）时会返回确认或错误响应。在这个模式下，如果发生正常的 Leader 选举，生产者会在选举时收到一个 <code>LeaderNotAvailableException</code> 异常，如果生产者能恰当地处理这个错误，它会重试发送悄息，最终消息会安全到达新的 Leader 那里。不过在这个模式下仍然有可能丢失数据，比如消息已经成功写入 Leader，但在消息被复制到 Follower 副本之前 Leader 发生崩溃。</p></li><li><p><code>acks = all</code>（这个和 <code>request.required.acks=-1</code> 含义一样）：意味着 Leader 在返回确认或错误响应之前，会等待所有同步副本都收到悄息。如果和 <code>min.insync.replicas</code> 参数结合起来，就可以决定在返回确认前至少有多少个副本能够收到悄息，生产者会一直重试直到消息被成功提交。不过这也是最慢的做法，因为生产者在继续发送其他消息之前需要等待所有副本都收到当前的消息。</p></li></ul><p>根据实际的应用场景，我们设置不同的 <code>acks</code>，以此保证数据的可靠性。</p><p>另外，Producer 发送消息还可以选择同步（默认，通过 <code>producer.type=sync</code> 配置） 或者异步（<code>producer.type=async</code>）模式。如果设置成异步，虽然会极大的提高消息发送的性能，但是这样会增加丢失数据的风险。如果需要确保消息的可靠性，必须将 <code>producer.type</code> 设置为 <code>sync</code>。</p></li><li><p>Leader 选举</p><p>在介绍 Leader 选举之前，让我们先来了解一下 ISR（in-sync replicas）列表。每个分区的 Leader 会维护一个 ISR 列表，ISR 列表里面就是 Follower 副本的 Broker 编号，只有跟得上 Leader 的 Follower 副本才能加入到 ISR 里面，这个是通过 <code>replica.lag.time.max.ms</code> 参数配置的。只有 ISR 里的成员才有被选为 Leader 的可能。</p></li></ul><blockquote><p><strong>数据一致性（可回答 Kafka 数据一致性原理？）</strong></p></blockquote><p>这里介绍的数据一致性主要是说不论是老的 Leader 还是新选举的 Leader，Consumer 都能读到一样的数据。那么 Kafka 是如何实现的呢？</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/kafka_highwatermark.png" alt="High Water Mark"></p><p>假设分区的副本为 3，其中副本 0 是 Leader，副本 1 和副本 2 是 Follower，并且在 ISR 列表里面。虽然副本 0 已经写入了 Message4，但是 Consumer 只能读取到 Message2。因为所有的 ISR 都同步了 Message2，只有 High Water Mark 以上的消息才支持 Consumer 读取，而 High Water Mark 取决于 ISR 列表里面偏移量最小的分区，对应于上图的副本 2，这个很类似于木桶原理。</p><p>这样做的原因是还没有被足够多副本复制的消息被认为是“不安全”的，如果 Leader 发生崩溃，另一个副本成为新 Leader，那么这些消息很可能丢失了。如果我们允许消费者读取这些消息，可能就会破坏一致性。试想，一个消费者从当前 Leader（副本 0）读取并处理了 Message4，这个时候 Leader 挂掉了，选举了副本 1 为新的 Leader，这时候另一个消费者再去从新的 Leader 读取消息，发现这个消息其实并不存在，这就导致了数据不一致性问题。</p><p>当然，引入了 High Water Mark 机制，会导致 Broker 间的消息复制因为某些原因变慢，那么消息到达消费者的时间也会随之变长（因为我们会先等待消息复制完毕）。延迟时间可以通过参数 <code>replica.lag.time.max.ms</code> 参数配置，它指定了副本在复制消息时可被允许的最大延迟时间。</p><h2 id="ISR、OSR、AR-是什么？"><a href="#ISR、OSR、AR-是什么？" class="headerlink" title="ISR、OSR、AR 是什么？"></a>ISR、OSR、AR 是什么？</h2><ul><li>ISR：In-Sync Replicas 副本同步队列</li><li>OSR：Out-of-Sync Replicas</li><li>AR：Assigned Replicas 所有副本</li></ul><p><code>AR = ISR + OSR</code></p><p>ISR 是由 Leader 维护，Follower 从 Leader 同步数据有一些延迟（具体可以参见 Kafka 的副本复制机制），超过相应的阈值会把 Follower 剔除出 ISR，存入 OSR（Out-of-Sync Replicas）列表，新加入的 Follower 也会先存放在 OSR 中。</p><h2 id="解释如何减少-ISR-中的扰动？broker-什么时候离开-ISR？"><a href="#解释如何减少-ISR-中的扰动？broker-什么时候离开-ISR？" class="headerlink" title="解释如何减少 ISR 中的扰动？broker 什么时候离开 ISR？"></a>解释如何减少 ISR 中的扰动？broker 什么时候离开 ISR？</h2><p>ISR 是一组与 Leader 完全同步的消息副本，也就是说 ISR 中包含了所有提交的消息。ISR 应该总是包含所有的副本，直到出现真正的故障。如果一个副本从 Leader 中脱离出来，将会从 ISR 中删除。</p><h2 id="如果副本在-ISR-中停留了很长时间表明什么？"><a href="#如果副本在-ISR-中停留了很长时间表明什么？" class="headerlink" title="如果副本在 ISR 中停留了很长时间表明什么？"></a>如果副本在 ISR 中停留了很长时间表明什么？</h2><p>如果一个副本在 ISR 中保留了很长一段时间，那么它就表明，跟踪器无法像在 Leader 收集数据那样快速地获取数据。</p><h2 id="请说明如果首选的副本不在-ISR-中会发生什么？"><a href="#请说明如果首选的副本不在-ISR-中会发生什么？" class="headerlink" title="请说明如果首选的副本不在 ISR 中会发生什么？"></a>请说明如果首选的副本不在 ISR 中会发生什么？</h2><p>如果首选的副本不在 ISR 中，控制器将无法将 leadership 转移到首选的副本。</p><h2 id="LEO、HW、LSO、LW-等分别代表什么？"><a href="#LEO、HW、LSO、LW-等分别代表什么？" class="headerlink" title="LEO、HW、LSO、LW 等分别代表什么？"></a>LEO、HW、LSO、LW 等分别代表什么？</h2><ul><li>LEO：是 LogEndOffset 的简称，代表当前日志文件中下一条。</li><li>HW：水位或水印（watermark）一词，也可称为高水位（high watermark），通常被用在流式处理领域（比如 Apache Flink、Apache Spark 等），以表征元素或事件在基于时间层面上的进度。在 Kafka 中，水位的概念反而与时间无关，而是与位置信息相关。严格来说，它表示的就是位置信息，即位移（offset）。取 partition 对应的 ISR 中最小的 LEO 作为 HW，consumer 最多只能消费到 HW 所在的位置上一条信息。</li><li>LSO：是 LastStableOffset 的简称，对未完成的事务而言，LSO 的值等于事务中第一条消息的位置（firstUnstableOffset），对已完成的事务而言，它的值同 HW 相同。</li><li>LW：Low Watermark 低水位，代表 AR 集合中最小的 logStartOffset 值。</li></ul><h2 id="数据传输的事务有几种？-请说明-Kafka-的消息投递保证（delivery-guarantee）机制以及如何实现？"><a href="#数据传输的事务有几种？-请说明-Kafka-的消息投递保证（delivery-guarantee）机制以及如何实现？" class="headerlink" title="数据传输的事务有几种？ / 请说明 Kafka 的消息投递保证（delivery guarantee）机制以及如何实现？"></a>数据传输的事务有几种？ / 请说明 Kafka 的消息投递保证（delivery guarantee）机制以及如何实现？</h2><p>数据传输的事务定义通常有以下三种级别：/ Kafka 支持三种消息投递语义：</p><ul><li>最多一次（At most once）：消息不会被重复发送，最多被传输一次，但也有可能一次不传输；</li><li>最少一次（At least one）：消息不会被漏发送，最少被传输一次，但也有可能被重复传输；</li><li>精确的一次（Exactly once）：不会漏传输也不会重复传输，每个消息都传输被接收。</li></ul><p>consumer 在从 broker 读取消息后，可以选择 commit，该操作会在 Zookeeper 中存下该 consumer 在该 partition 下读取的消息的 offset，该 consumer 下一次再读该 partition 时会从下一条开始读取。如未 commit，下一次读取的开始位置会跟上一次 commit 之后的开始位置相同。</p><p>可以将 consumer 设置为 autocommit，即 consumer 一旦读到数据立即自动 commit。如果只讨论这一读取消息的过程，那 Kafka 是确保了 Exactly once。但实际上实际使用中 consumer 并非读取完数据就结束了，而是要进行进一步处理，而数据处理与 commit 的顺序在很大程度上决定了消息从 broker 和 consumer 的 delivery guarantee semantic。</p><p>读完消息先 commit 再处理消息。这种模式下，如果 consumer 在 commit 后还没来得及处理消息就 crash 了，下次重新开始工作后就无法读到刚刚已提交而未处理的消息，这就对应于 At most once。</p><p>读完消息先处理再 commit 消费状态（保存 offset）。这种模式下，如果在处理完消息之后 commit 之前 Consumer crash 了，下次重新开始工作时还会处理刚刚未 commit 的消息，实际上该消息已经被处理过了，这就对应于 At least once。</p><p>如果一定要做到 Exactly once，就需要协调 offset 和实际操作的输出。经典的做法是引入两阶段提交，但由于许多输出系统不支持两阶段提交，更为通用的方式是将 offset 和操作输入存在同一个地方。比如，consumer 拿到数据后可能把数据放到 HDFS，如果把最新的 offset 和数据本身一起写到 HDFS，那就可以保证数据的输出和 offset 的更新要么都完成，要么都不完成，间接实现 Exactly once。（目前就 high level API 而言，offset 是存于 Zookeeper 中的，无法存于 HDFS，而 low level API 的 offset 是由自己去维护的，可以将之存于 HDFS 中）。</p><p>总之，Kafka 默认保证 At least once，并且允许通过设置 producer 异步提交来实现 At most once，而 Exactly once 要求与目标存储系统协作，Kafka 提供的 offset 可以较为容易地实现这种方式。</p><h2 id="Kafka-有可能在生产后发生消息偏移吗？"><a href="#Kafka-有可能在生产后发生消息偏移吗？" class="headerlink" title="Kafka 有可能在生产后发生消息偏移吗？"></a>Kafka 有可能在生产后发生消息偏移吗？</h2><p>在大多数队列系统中，作为生产者的类无法做到这一点，它的作用是触发并忘记消息。broker 将完成剩下的工作，比如使用 id 进行适当的元数据处理、偏移量等。</p><p>作为消息的用户，你可以从 Kafka broker 中获得补偿。如果你注视 <code>SimpleConsumer</code> 类，你会注意到它会获取包括偏移量作为列表的 <code>MultiFetchResponse</code> 对象。此外，当你对 Kafka 消息进行迭代时，你会拥有包括偏移量和消息发送的 <code>MessageAndOffset</code> 对象。</p><h2 id="Kafka-消费者是否可以消费指定分区消息？"><a href="#Kafka-消费者是否可以消费指定分区消息？" class="headerlink" title="Kafka 消费者是否可以消费指定分区消息？"></a>Kafka 消费者是否可以消费指定分区消息？</h2><p>Kafka consumer 消费消息时，向 broker 发出 fetch 请求去消费特定分区的消息，consumer 指定消息在日志中的偏移量（offset），就可以消费从这个位置开始的消息，customer 拥有了 offset 的控制权，可以向后回滚去重新消费之前的消息，这是很有意义的。</p><h2 id="Kafka-消息是采用-Pull-模式，还是-Push-模式？"><a href="#Kafka-消息是采用-Pull-模式，还是-Push-模式？" class="headerlink" title="Kafka 消息是采用 Pull 模式，还是 Push 模式？"></a>Kafka 消息是采用 Pull 模式，还是 Push 模式？</h2><p>Kafka 最初考虑的问题是，customer 应该从 brokers 拉取消息还是 brokers 将消息推送到 consumer，也就是 pull 还是 push。在这方面，Kafka 遵循了一种大部分消息系统共同的传统的设计：producer 将消息推送到 broker，consumer 从 broker 拉取消息。</p><p>一些消息系统比如 Scribe 和 Apache Flume 采用了 push 模式，将消息推送到下游的 consumer。这样做有好处也有坏处：由 broker 决定消息推送的速率，对于不同消费速率的 consumer 就不太好处理了。消息系统都致力于让 consumer 以最大的速率最快速的消费消息，但不幸的是，push 模式下，当 broker 推送的速率远大于 consumer 消费的速率时，consumer 恐怕就要崩溃了。最终 Kafka 还是选取了传统的 pull 模式。</p><p>Pull 模式的另外一个好处是 consumer 可以自主决定是否批量的从 broker 拉取数据。Push 模式必须在不知道下游 consumer 消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免 consumer 崩溃而采用较低的推送速率，将可能导致一次只推送较少的消息而造成浪费。Pull 模式下，consumer 就可以根据自己的消费能力去决定这些策略。 Pull 有个缺点是，如果 broker 没有可供消费的消息，将导致 consumer 不断在循环中轮询，直到新消息到达。为了避免这点，Kafka 有个参数可以让 consumer 阻塞知道新消息到达（当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发送）。</p><h2 id="Kafka-高效文件存储设计特点？"><a href="#Kafka-高效文件存储设计特点？" class="headerlink" title="Kafka 高效文件存储设计特点？"></a>Kafka 高效文件存储设计特点？</h2><ul><li>Kafka 把 topic 中一个 partition 大文件分成多个小文件段，通过多个小文件段，就容易定期清除或删除已经消费完文件，减少磁盘占用。</li><li>通过索引信息可以快速定位 message 和确定 response 的最大大小。</li><li>通过 index 元数据全部映射到 memory，可以避免 segment file 的 IO 磁盘操作。</li><li>通过索引文件稀疏存储，可以大幅降低 index 文件元数据占用空间大小。</li></ul><h2 id="Kafka-创建-Topic-时如何将分区放置到不同的-Broker-中？"><a href="#Kafka-创建-Topic-时如何将分区放置到不同的-Broker-中？" class="headerlink" title="Kafka 创建 Topic 时如何将分区放置到不同的 Broker 中？"></a>Kafka 创建 Topic 时如何将分区放置到不同的 Broker 中？</h2><ul><li>副本因子不能大于 Broker 的个数。</li><li>第一个分区（编号为 0）的第一个副本放置位置是随机从 brokerList 选择的。</li><li>其他分区的第一个副本放置位置相对于第 0 个分区依次往后移。也就是如果我们有 5 个 Broker，5 个分区，假设第一个分区放在第四个 Broker 上，那么第二个分区将会放在第五个 Broker 上；第三个分区将会放在第一个 Broker 上；第四个分区将会放在第二个 Broker 上，依次类推。</li><li>剩余的副本相对于第一个副本放置位置其实是由 nextReplicaShift 决定的，而这个数也是随机产生的。</li></ul><h2 id="Kafka-新建的分区会在哪个目录下创建？"><a href="#Kafka-新建的分区会在哪个目录下创建？" class="headerlink" title="Kafka 新建的分区会在哪个目录下创建？"></a>Kafka 新建的分区会在哪个目录下创建？</h2><p>我们知道，在启动 Kafka 集群之前，我们需要配置好 <code>log.dirs</code> 参数，其值是 Kafka 数据的存放目录，这个参数可以配置多个目录，目录之间使用逗号分隔，通常这些目录是分布在不同的磁盘上用于提高读写性能。当然我们也可以配置 <code>log.dir</code> 参数，含义一样。只需要设置其中一个即可。</p><p>如果 <code>log.dirs</code> 参数只配置了一个目录，那么分配到各个 Broker 上的分区肯定只能在这个目录下创建文件夹用于存放数据。</p><p>但是如果 <code>log.dirs</code> 参数配置了多个目录，那么 Kafka 会在哪个文件夹中创建分区目录呢？</p><p>答案是：Kafka 会在含有分区目录最少的文件夹中创建新的分区目录，分区目录名为 “Topic 名 + 分区 ID”。注意，是分区文件夹总数最少的目录，而不是磁盘使用量最少的目录！也就是说，如果你给 <code>log.dirs</code> 参数新增了一个新的磁盘，新的分区目录肯定是先在这个新的磁盘上创建直到这个新的磁盘目录拥有的分区目录不是最少为止。</p><h2 id="谈一谈-Kafka-的再均衡？"><a href="#谈一谈-Kafka-的再均衡？" class="headerlink" title="谈一谈 Kafka 的再均衡？"></a>谈一谈 Kafka 的再均衡？</h2><p>在 Kafka 中，当有新消费者加入或者订阅的 topic 数发生变化时，会触发 Rebalance（再均衡：在同一个消费者组当中，分区的所有权从一个消费者转移到另外一个消费者）机制，Rebalance 顾名思义就是重新均衡消费者消费。Rebalance 的过程如下：</p><ol><li>第一步：所有成员都向 Coordinator 发送请求，请求入组。一旦所有成员都发送了请求，Coordinator 会从中选择一个 consumer 担任 Leader 的角色，并把组成员信息以及订阅信息发给 Leader。</li><li>第二步：Leader 开始分配消费方案，指明具体哪个 consumer 负责消费哪些 topic 的哪些 partition。一旦完成分配，Leader 会将这个方案发给 Coordinator。Coordinator 接收到分配方案之后会把方案发给各个 consumer，这样组内的所有成员就都知道自己应该消费哪些分区了。</li></ol><p>所以对于 Rebalance 来说，Coordinator 起着至关重要的作用。</p><h2 id="Kafka-分区分配策略"><a href="#Kafka-分区分配策略" class="headerlink" title="Kafka 分区分配策略"></a>Kafka 分区分配策略</h2><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/kafka_partition.png" alt="Kafka 分区分配策略"></p><p>在 Kafka 内部存在两种默认的分区分配策略：Range 和 RoundRobin。当以下事件发生时，Kafka 将会进行一次分区分配：</p><ul><li>同一个 Consumer Group 内新增消费者。</li><li>消费者离开当前所属的 Consumer Group，包括 shuts down 或 crashes。</li><li>订阅的主题新增分区。</li></ul><p>将分区的所有权从一个消费者移到另一个消费者称为重新平衡（Rebalance），如何 Rebalance 就涉及到下面提到的分区分配策略。下面我们将详细介绍 Kafka 内置的两种分区分配策略。本文假设我们有个名为 T1 的主题，其包含了 10 个分区，然后我们有两个消费者（C1，C2）来消费这 10 个分区里面的数据，而且 C1 的 <code>num.streams = 1</code>，C2 的 <code>num.streams = 2</code>。</p><p><strong>Range strategy</strong></p><p>Range 策略是对每个主题而言的，首先对同一个主题里面的分区按照序号进行排序，并对消费者按照字母顺序进行排序。在我们的例子里面，排完序的分区将会是 <code>0, 1, 2, 3, 4, 5, 6, 7, 8, 9</code>；消费者线程排完序将会是 <code>C1-0, C2-0, C2-1</code>。然后将 partitions 的个数除于消费者线程的总数来决定每个消费者线程消费几个分区。如果除不尽，那么前面几个消费者线程将会多消费一个分区。</p><p>在我们的例子里面，我们有 10 个分区，3 个消费者线程，10/3 = 3，而且除不尽，那么消费者线程 C1-0 将会多消费一个分区，所以最后分区分配的结果看起来是这样的：</p><ul><li>C1-0 将消费 0, 1, 2, 3 分区</li><li>C2-0 将消费 4, 5, 6 分区</li><li>C2-1 将消费 7, 8, 9 分区</li></ul><p>假如我们有 11 个分区，那么最后分区分配的结果看起来是这样的：</p><ul><li>C1-0 将消费 0, 1, 2, 3 分区</li><li>C2-0 将消费 4, 5, 6, 7 分区</li><li>C2-1 将消费 8, 9, 10 分区</li></ul><p>假如我们有 2 个主题（T1 和 T2），分别有 10 个分区，那么最后分区分配的结果看起来是这样的：</p><ul><li>C1-0 将消费 T1 主题的 0, 1, 2, 3 分区以及 T2 主题的 0, 1, 2, 3 分区</li><li>C2-0 将消费 T1 主题的 4, 5, 6 分区以及 T2 主题的 4, 5, 6 分区</li><li>C2-1 将消费 T1 主题的 7, 8, 9 分区以及 T2 主题的 7, 8, 9 分区</li></ul><p>可以看出，C1-0 消费者线程比其他消费者线程多消费了 2 个分区，这就是 Range strategy 的一个很明显的弊端。</p><p><strong>RoundRobin strategy</strong></p><p>使用 RoundRobin 策略有两个前提条件必须满足：</p><ul><li>同一个 Consumer Group 里面的所有消费者的 <code>num.streams</code>必须相等；</li><li>每个消费者订阅的主题必须相同。</li></ul><p>所以这里假设前面提到的 2 个消费者的 <code>num.streams = 2</code>。</p><p>RoundRobin 策略的工作原理：将所有主题的分区组成 TopicAndPartition 列表，然后对 TopicAndPartition 列表按照 hashCode 进行排序，这里文字可能说不清，看下面的代码应该会明白：</p><p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> allTopicPartitions = ctx.partitionsForTopic.flatMap &#123;</span><br><span class="line">    <span class="keyword">case</span>(topic, partitions) =&gt; info(<span class="string">&quot;Consumer %s rebalancing the following partitions for topic %s: %s&quot;</span>.format(ctx.consumerId, topic, partitions))</span><br><span class="line">    partitions.map(partition =&gt; &#123;</span><br><span class="line">        <span class="type">TopicAndPartition</span>(topic, partition)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;.toSeq.sortWith((topicPartition1, topicPartition2) =&gt; &#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    * Randomize the order by taking the hashcode to reduce the likelihood of all partitions of a given topic ending</span></span><br><span class="line"><span class="comment">    * up on one consumer (if it has a high enough stream count).</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    topicPartition1.toString.hashCode &lt; topicPartition2.toString.hashCode</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p><p>最后按照 round-robin 风格将分区分别分配给不同的消费者线程。</p><p>在我们的例子里面，假如按照 hashCode 排序完的 topic-partitions 组依次为 T1-5, T1-3, T1-0, T1-8, T1-2, T1-1, T1-4, T1-7, T1-6, T1-9，我们的消费者线程排序为 C1-0, C1-1, C2-0, C2-1，最后分区分配的结果为：</p><ul><li>C1-0 将消费 T1-5, T1-2, T1-6 分区；</li><li>C1-1 将消费 T1-3, T1-1, T1-9 分区；</li><li>C2-0 将消费 T1-0, T1-4 分区；</li><li>C2-1 将消费 T1-8, T1-7 分区。</li></ul><p>多个主题的分区分配和单个主题类似。</p><h2 id="Kafka-是如何实现高吞吐率的？"><a href="#Kafka-是如何实现高吞吐率的？" class="headerlink" title="Kafka 是如何实现高吞吐率的？"></a>Kafka 是如何实现高吞吐率的？</h2><p>Kafka 是分布式消息系统，需要处理海量的消息，Kafka 的设计是把所有的消息都写入速度低容量大的硬盘，以此来换取更强的存储能力，但实际上，使用硬盘并没有带来过多的性能损失。kafka 主要使用了以下几个方式实现了超高的吞吐率：</p><ul><li>顺序读写</li><li>零拷贝</li><li>文件分段</li><li>批量发送</li><li>数据压缩</li></ul><h2 id="Kafka-缺点？"><a href="#Kafka-缺点？" class="headerlink" title="Kafka 缺点？"></a>Kafka 缺点？</h2><ul><li>由于是批量发送，数据并非真正的实时；</li><li>对于 mqtt 协议不支持；</li><li>不支持物联网传感数据直接接入；</li><li>仅支持统一分区内消息有序，无法实现全局消息有序；</li><li>监控不完善，需要安装插件；</li><li>依赖 Zookeeper 进行元数据管理。</li></ul><h2 id="Kafka-新旧消费者的区别？"><a href="#Kafka-新旧消费者的区别？" class="headerlink" title="Kafka 新旧消费者的区别？"></a>Kafka 新旧消费者的区别？</h2><p>旧的 Kafka 消费者 API 主要包括：SimpleConsumer（简单消费者） 和 ZookeeperConsumerConnector（高级消费者）。SimpleConsumer 名字看起来是简单消费者，但是其实用起来很不简单，可以使用它从特定的分区和偏移量开始读取消息。高级消费者和现在新的消费者有点像，有消费者群组，有分区再均衡，不过它使用 ZK 来管理消费者群组，并不具备偏移量和再均衡的可操控性。</p><p>现在的消费者同时支持以上两种行为，所以为啥还用旧消费者 API 呢？</p><h2 id="Kafka-分区数可以增加或减少吗？为什么？"><a href="#Kafka-分区数可以增加或减少吗？为什么？" class="headerlink" title="Kafka 分区数可以增加或减少吗？为什么？"></a>Kafka 分区数可以增加或减少吗？为什么？</h2><p>我们可以使用 <code>bin/kafka-topics.sh</code> 命令对 Kafka 增加 Kafka 的分区数据，但是 Kafka 不支持减少分区数。Kafka 分区数据不支持减少是由很多原因的，比如减少的分区其数据放到哪里去？是删除，还是保留？删除的话，那么这些没消费的消息不就丢了。如果保留这些消息如何放到其他分区里面？追加到其他分区后面的话那么就破坏了 Kafka 单个分区的有序性。如果要保证删除分区数据插入到其他分区保证有序性，那么实现起来逻辑就会非常复杂。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Kafka/">Kafka</category>
      
      
      <comments>https://blog.eurkon.com/post/89cabcb1.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Flume 面试题解析</title>
      <link>https://blog.eurkon.com/post/5a2a12a6.html</link>
      <guid>https://blog.eurkon.com/post/5a2a12a6.html</guid>
      <pubDate>Mon, 29 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Flume-使用场景？&quot;&gt;&lt;a href=&quot;#Flume-使用场景？&quot; class=&quot;headerlink&quot; title=&quot;Flume 使用场景？&quot;&gt;&lt;/a&gt;Flume 使用场景？&lt;/h2&gt;&lt;p&gt;线上数据一般主要是落地（存储到磁盘）或者通过 socket 传输给另</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Flume-使用场景？"><a href="#Flume-使用场景？" class="headerlink" title="Flume 使用场景？"></a>Flume 使用场景？</h2><p>线上数据一般主要是落地（存储到磁盘）或者通过 socket 传输给另外一个系统，这种情况下，你很难推动线上应用或服务去修改接口，实现直接向 Kafka 里写数据，这时候你可能就需要 Flume 这样的系统帮你去做传输。</p><h2 id="Flume-丢包问题？"><a href="#Flume-丢包问题？" class="headerlink" title="Flume 丢包问题？"></a>Flume 丢包问题？</h2><p>单机 upd 的 Flume source 的配置，100+M/s 数据量，10w qps Flume 就开始大量丢包，因此很多公司在搭建系统时，抛弃了 Flume，自己研发传输系统，但是往往会参考 Flume 的 Source-Channel-Sink 模式。</p><p>一些公司在 Flume 工作过程中，会对业务日志进行监控，例如 Flume agent 中有多少条日志，Flume 到 Kafka 后有多少条日志等等，如果数据丢失保持在 1% 左右是没有问题的，当数据丢失达到 5% 左右时就必须采取相应措施。</p><h2 id="Flume-与-Kafka-的选取？"><a href="#Flume-与-Kafka-的选取？" class="headerlink" title="Flume 与 Kafka 的选取？"></a>Flume 与 Kafka 的选取？</h2><p>采集层主要可以使用 Flume、Kafka 两种技术。</p><ul><li>Flume：Flume 是管道流方式，提供了很多的默认实现，让用户通过参数部署，及扩展 API。</li><li>Kafka：Kafka 是一个可持久化的分布式的消息队列。</li></ul><p>Kafka 是一个非常通用的系统。你可以有许多生产者和很多的消费者共享多个主题 Topics。相比之下，Flume 是一个专用工具被设计为旨在往 HDFS，HBase 发送数据。它对 HDFS 有特殊的优化，并且集成了 Hadoop 的安全特性。所以，Cloudera 建议如果数据被多个系统消费的话，使用 Kafka；如果数据被设计给 Hadoop 使用，使用 Flume。</p><p>正如你们所知 Flume 内置很多的 source 和 sink 组件。然而，Kafka 明显有一个更小的生产消费者生态系统，并且 Kafka 的社区支持不好。希望将来这种情况会得到改善，但是目前：使用 Kafka 意味着你准备好了编写你自己的生产者和消费者代码。如果已经存在的 Flume Sources 和 Sinks 满足你的需求，并且你更喜欢不需要任何开发的系统，请使用 Flume。</p><p>Flume 可以使用拦截器实时处理数据。这些对数据屏蔽或者过量是很有用的。Kafka 需要外部的流处理系统才能做到。</p><p>Kafka 和 Flume 都是可靠的系统，通过适当的配置能保证零数据丢失。然而，Flume 不支持副本事件。于是，如果 Flume 代理的一个节点奔溃了，即使使用了可靠的文件管道方式，你也将丢失这些事件直到你恢复这些磁盘。如果你需要一个高可靠性的管道，那么使用 Kafka 是个更好的选择。</p><p>Flume 和 Kafka 可以很好地结合起来使用。如果你的设计需要从 Kafka 到 Hadoop 的流数据，使用 Flume 代理并配置 Kafka 的 Source 读取数据也是可行的：你没有必要实现自己的消费者。你可以直接利用 Flume 与 HDFS 及 HBase 的结合的所有好处。你可以使用 Cloudera Manager 对消费者的监控，并且你甚至可以添加拦截器进行一些流处理。</p><h2 id="数据怎么采集到-Kafka，实现方式？"><a href="#数据怎么采集到-Kafka，实现方式？" class="headerlink" title="数据怎么采集到 Kafka，实现方式？"></a>数据怎么采集到 Kafka，实现方式？</h2><p>使用官方提供的 FlumeKafka 插件，插件的实现方式是自定义了 Flume 的 sink，将数据从 channel 中取出，通过 Kafka 的 producer 写入到 Kafka 中，可以自定义分区等。</p><h2 id="Flume-管道内存，Flume-宕机了数据丢失怎么解决？"><a href="#Flume-管道内存，Flume-宕机了数据丢失怎么解决？" class="headerlink" title="Flume 管道内存，Flume 宕机了数据丢失怎么解决？"></a>Flume 管道内存，Flume 宕机了数据丢失怎么解决？</h2><ul><li>Flume 的 channel 分为很多种，可以将数据写入到文件。</li><li>防止非首个 agent 宕机的方法可以做集群或者主备。</li></ul><h2 id="Flume-配置方式，Flume-集群？"><a href="#Flume-配置方式，Flume-集群？" class="headerlink" title="Flume 配置方式，Flume 集群？"></a>Flume 配置方式，Flume 集群？</h2><p>Flume 的配置围绕着 source、channel、sink 叙述，Flume 的集群是做在 agent 上的，而非机器上。</p><h2 id="Flume-不采集-Nginx-日志，通过-Logger4j-采集日志，优缺点是什么？"><a href="#Flume-不采集-Nginx-日志，通过-Logger4j-采集日志，优缺点是什么？" class="headerlink" title="Flume 不采集 Nginx 日志，通过 Logger4j 采集日志，优缺点是什么？"></a>Flume 不采集 Nginx 日志，通过 Logger4j 采集日志，优缺点是什么？</h2><ul><li>优点：Nginx 的日志格式是固定的，但是缺少 sessionId，通过 logger4j 采集的日志是带有 sessionId 的，而 session 可以通过 redis 共享，保证了集群日志中的同一 session 落到不同的 tomcat 时，sessionId 还是一样的，而且 logger4j 的方式比较稳定，不会宕机。</li><li>缺点：不够灵活，logger4j 的方式和项目结合过于紧密，而 Flume 的方式比较灵活，拔插式比较好，不会影响项目性能。</li></ul><h2 id="Flume-和-Kafka-采集日志区别，采集日志时中间停了，怎么记录之前的日志？"><a href="#Flume-和-Kafka-采集日志区别，采集日志时中间停了，怎么记录之前的日志？" class="headerlink" title="Flume 和 Kafka 采集日志区别，采集日志时中间停了，怎么记录之前的日志？"></a>Flume 和 Kafka 采集日志区别，采集日志时中间停了，怎么记录之前的日志？</h2><ul><li>Flume 采集日志是通过流的方式直接将日志收集到存储层，而 Kafka 是将缓存在 Kafka 集群，待后期可以采集到存储层。</li><li>Flume 采集中间停了，可以采用文件的方式记录之前的日志，而 Kafka 是采用 offset 的方式记录之前的日志。</li></ul><h2 id="Flume-有哪些组件，Flume-的-source、channel、sink-具体是做什么的？"><a href="#Flume-有哪些组件，Flume-的-source、channel、sink-具体是做什么的？" class="headerlink" title="Flume 有哪些组件，Flume 的 source、channel、sink 具体是做什么的？"></a>Flume 有哪些组件，Flume 的 source、channel、sink 具体是做什么的？</h2><ul><li>source：用于采集数据，Source 是产生数据流的地方，同时 Source 会将产生的数据流传输到 Channel，这个有点类似于 Java IO 部分的 Channel。</li><li>channel：用于桥接 Sources 和 Sinks，类似于一个队列。</li><li>sink：从 Channel 收集数据，将数据写到目标源（可以是下一个 Source，也可以是 HDFS 或者 HBase）。</li></ul><p><strong>注意：</strong>要熟悉 source、channel、sink 的类型</p><p>单一结构：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/flume_module.png" alt="单一结构"></p><p>复杂架构：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/flume_multi_module.png" alt="复杂架构："></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Flume/">Flume</category>
      
      
      <comments>https://blog.eurkon.com/post/5a2a12a6.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Zookeeper 面试题解析</title>
      <link>https://blog.eurkon.com/post/2f1ea7f2.html</link>
      <guid>https://blog.eurkon.com/post/2f1ea7f2.html</guid>
      <pubDate>Sat, 27 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;请简述-Zookeeper-的选举机制？&quot;&gt;&lt;a href=&quot;#请简述-Zookeeper-的选举机制？&quot; class=&quot;headerlink&quot; title=&quot;请简述 Zookeeper 的选举机制？&quot;&gt;&lt;/a&gt;请简述 Zookeeper 的选举机制？&lt;/h2&gt;&lt;</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="请简述-Zookeeper-的选举机制？"><a href="#请简述-Zookeeper-的选举机制？" class="headerlink" title="请简述 Zookeeper 的选举机制？"></a>请简述 Zookeeper 的选举机制？</h2><p>假设有五台服务器组成的 Zookeeper 集群，它们的 id 从 1-5，同时它们都是最新启动的，也就是没有历史数据，在存放数据量这一点上，都是一样的。假设这些服务器依序启动，来看看会发生什么。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/zookeeper_leader.png" alt="Zookeeper 选举机制"></p><ol><li>服务器 1 启动，此时只有它一台服务器启动了，它发出去的报没有任何响应，所以它的选举状态一直是 <code>LOOKING</code> 状态；</li><li>服务器 2 启动，它与最开始启动的服务器 1 进行通信，互相交换自己的选举结果，由于两者都没有历史数据，所以 id 值较大的服务器 2 胜出，但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是 3)，所以服务器 1、2 还是继续保持 <code>LOOKING</code> 状态；</li><li>服务器 3 启动，根据前面的理论分析，服务器 3 成为服务器 1、2、3 中的 <code>Leader</code>，而与上面不同的是，此时有三台服务器选举了它，所以它成为了这次选举的 <code>Leader</code>；</li><li>服务器 4 启动，根据前面的分析，理论上服务器 4 应该是服务器 1、2、3、4 中最大的，但是由于前面已经有半数以上的服务器选举了服务器 3，所以它成为 <code>Follower</code>；</li><li>服务器 5 启动，同 4 一样成为 <code>Follower</code>。</li></ol><p><strong>注意：</strong>如果按照 5，4，3，2，1 的顺序启动，那么 5 将成为 <code>Leader</code>，因为在满足半数条件后，ZooKeeper 集群启动，5 的 Id 最大，被选举为 <code>Leader</code>。</p><h2 id="客户端如何正确处理-CONNECTIONLOSS（连接断开）和-SESSIONEXPIRED（Session-过期）两类连接异常？"><a href="#客户端如何正确处理-CONNECTIONLOSS（连接断开）和-SESSIONEXPIRED（Session-过期）两类连接异常？" class="headerlink" title="客户端如何正确处理 CONNECTIONLOSS（连接断开）和 SESSIONEXPIRED（Session 过期）两类连接异常？"></a>客户端如何正确处理 CONNECTIONLOSS（连接断开）和 SESSIONEXPIRED（Session 过期）两类连接异常？</h2><p>在 ZooKeeper 中，服务器和客户端之间维持的是一个长连接，在 SESSION_TIMEOUT 时间内，服务器会确定客户端是否正常连接（客户端会定时向服务器发送 heart_beat），服务器重置下次 SESSION_TIMEOUT 时间。因此，在正常情况下，Session 一直有效，并且 ZK 集群所有机器上都保存这个 Session 信息。在出现问题的情况下，客户端与服务器之间连接断了（客户端所连接的那台 ZK 机器挂了，或是其它原因的网络闪断），这个时候客户端会主动在地址列表（初始化的时候传入构造方法的那个参数 connectString）中选择新的地址进行连接。</p><p>以上即为服务器与客户端之间维持长连接的过程，在这个过程中，用户可能会看到两类异常 CONNECTIONLOSS（连接断开）和 SESSIONEXPIRED（Session 过期）。</p><p>发生 CONNECTIONLOSS 后，此时用户不需要关心我的会话是否可用，应用所要做的就是等待客户端帮我们自动连接上新的 ZK 机器，一旦成功连接上新的 ZK 机器后，确认之前的操作是否执行成功了。</p><h2 id="一个客户端修改了某个节点的数据，其他客户端能够马上获取到这个最新数据吗？"><a href="#一个客户端修改了某个节点的数据，其他客户端能够马上获取到这个最新数据吗？" class="headerlink" title="一个客户端修改了某个节点的数据，其他客户端能够马上获取到这个最新数据吗？"></a>一个客户端修改了某个节点的数据，其他客户端能够马上获取到这个最新数据吗？</h2><p>ZooKeeper 不能确保任何客户端能够获取（即 Read Request）到一样的数据，除非客户端自己要求，方法是客户端在获取数据之前调用 <code>org.apache.zookeeper.AsyncCallback.VoidCallback, java.lang.Object) sync</code>。</p><ul><li>通常情况下（这里所说的通常情况满足：1. 对获取的数据是否是最新版本不敏感，2. 一个客户端修改了数据，其它客户端是否需要立即能够获取最新数据），可以不关心这点。</li><li>在其它情况下，最清晰的场景是这样：ZK 客户端 A 对 <code>/my_test</code> 的内容从 v1-&gt;v2, 但是 ZK 客户端 B 对 <code>/my_test</code> 的内容获取，依然得到的是 v1。请注意，这个是实际存在的现象，当然延时很短。解决的方法是客户端 B 先调用 <code>sync()</code>, 再调用 <code>getData()</code>。</li></ul><h2 id="ZooKeeper-对节点的-watch-监听是永久的吗？为什么？"><a href="#ZooKeeper-对节点的-watch-监听是永久的吗？为什么？" class="headerlink" title="ZooKeeper 对节点的 watch 监听是永久的吗？为什么？"></a>ZooKeeper 对节点的 watch 监听是永久的吗？为什么？</h2><p>不是。</p><p>官方声明：一个 Watch 事件是一个一次性的触发器，当被设置了 Watch 的数据发生了改变的时候，则服务器将这个改变发送给设置了 Watch 的客户端，以便通知它们。</p><p>为什么不是永久的，举个例子，如果服务端变动频繁，而监听的客户端很多情况下，每次变动都要通知到所有的客户端，这太消耗性能了。</p><p>一般是客户端执行 <code>getData(&quot;/节点 A&quot;,true)</code>，如果节点 A 发生了变更或删除，客户端会得到它的 watch 事件，但是在之后节点 A 又发生了变更，而客户端又没有设置 watch 事件，就不再给客户端发送。</p><p>在实际应用中，很多情况下，我们的客户端不需要知道服务端的每一次变动，我只要最新的数据即可。</p><h2 id="ZooKeeper-中使用-watch-的注意事项有哪些？"><a href="#ZooKeeper-中使用-watch-的注意事项有哪些？" class="headerlink" title="ZooKeeper 中使用 watch 的注意事项有哪些？"></a>ZooKeeper 中使用 watch 的注意事项有哪些？</h2><p>使用 watch 需要注意的几点：</p><ul><li>Watches 通知是一次性的，必须重复注册。</li><li>发生 CONNECTIONLOSS 之后，只要在 session_timeout 之内再次连接上（即不发生 SESSIONEXPIRED），那么这个连接注册的 watches 依然在。</li><li>节点数据的版本变化会触发 NodeDataChanged，注意，这里特意说明了是版本变化。存在这样的情况，只要成功执行了 <code>setData()</code> 方法，无论内容是否和之前一致，都会触发 NodeDataChanged。</li><li>对某个节点注册了 watch，但是节点被删除了，那么注册在这个节点上的 watches 都会被移除。</li><li>同一个 ZK 客户端对某一个节点注册相同的 watch，只会收到一次通知。</li><li>Watcher 对象只会保存在客户端，不会传递到服务端。</li></ul><h2 id="能否收到每次节点变化的通知？"><a href="#能否收到每次节点变化的通知？" class="headerlink" title="能否收到每次节点变化的通知？"></a>能否收到每次节点变化的通知？</h2><p>如果节点数据的更新频率很高的话，不能。</p><p>原因在于：当一次数据修改，通知客户端，客户端再次注册 watch，在这个过程中，可能数据已经发生了许多次数据修改，因此，千万不要做这样的测试：“数据被修改了 n 次，一定会收到 n 次通知”来测试 server 是否正常工作。</p><h2 id="能否为临时节点创建子节点？"><a href="#能否为临时节点创建子节点？" class="headerlink" title="能否为临时节点创建子节点？"></a>能否为临时节点创建子节点？</h2><p>ZooKeeper 中不能为临时节点创建子节点，如果需要创建子节点，应该将要创建子节点的节点创建为永久性节点。</p><h2 id="是否可以拒绝单个-IP-对-ZooKeeper-的访问？"><a href="#是否可以拒绝单个-IP-对-ZooKeeper-的访问？" class="headerlink" title="是否可以拒绝单个 IP 对 ZooKeeper 的访问？"></a>是否可以拒绝单个 IP 对 ZooKeeper 的访问？</h2><p>如何实现？ZK 本身不提供这样的功能，它仅仅提供了对单个 IP 的连接数的限制。你可以通过修改 iptables 来实现对单个 ip 的限制。</p><h2 id="创建的临时节点什么时候会被删除，是连接一断就删除吗？"><a href="#创建的临时节点什么时候会被删除，是连接一断就删除吗？" class="headerlink" title="创建的临时节点什么时候会被删除，是连接一断就删除吗？"></a>创建的临时节点什么时候会被删除，是连接一断就删除吗？</h2><p>延时是多少？连接断了之后，ZK 不会马上移除临时数据，只有当 SESSIONEXPIRED 之后，才会把这个会话建立的临时数据移除。因此，用户需要谨慎设置 Session_TimeOut。</p><h2 id="ZooKeeper-是否支持动态进行机器扩容？如果目前不支持，那么要如何扩容呢？"><a href="#ZooKeeper-是否支持动态进行机器扩容？如果目前不支持，那么要如何扩容呢？" class="headerlink" title="ZooKeeper 是否支持动态进行机器扩容？如果目前不支持，那么要如何扩容呢？"></a>ZooKeeper 是否支持动态进行机器扩容？如果目前不支持，那么要如何扩容呢？</h2><p>ZooKeeper 中的动态扩容其实就是水平扩容，Zookeeper 对这方面的支持不太好，目前有两种方式：</p><ul><li>全部重启：关闭所有 Zookeeper 服务，修改配置之后启动，不影响之前客户端的会话。</li><li>逐个重启：这是比较常用的方式。</li></ul><h2 id="ZooKeeper-集群中服务器之间是怎样通信的？"><a href="#ZooKeeper-集群中服务器之间是怎样通信的？" class="headerlink" title="ZooKeeper 集群中服务器之间是怎样通信的？"></a>ZooKeeper 集群中服务器之间是怎样通信的？</h2><p>Leader 服务器会和每一个 Follower/Observer 服务器都建立 TCP 连接，同时为每个 F/O 都创建一个叫做 LearnerHandler 的实体。LearnerHandler 主要负责 Leader 和 F/O 之间的网络通讯，包括数据同步，请求转发和 Proposal 提议的投票等。Leader 服务器保存了所有 F/O 的 LearnerHandler。</p><h2 id="ZooKeeper-是否会自动进行日志清理？"><a href="#ZooKeeper-是否会自动进行日志清理？" class="headerlink" title="ZooKeeper 是否会自动进行日志清理？"></a>ZooKeeper 是否会自动进行日志清理？</h2><p>如何进行日志清理？ZK 自己不会进行日志清理，需要运维人员进行日志清理。</p><h2 id="谈谈你对-ZooKeeper-的理解？"><a href="#谈谈你对-ZooKeeper-的理解？" class="headerlink" title="谈谈你对 ZooKeeper 的理解？"></a>谈谈你对 ZooKeeper 的理解？</h2><ul><li><p>Zookeeper 作为一个分布式的服务框架，主要用来解决分布式集群中应用系统的一致性问题。ZooKeeper 提供的服务包括：<strong>分布式消息同步和协调机制</strong>、<strong>服务器节点动态上下线</strong>、<strong>统一配置管理</strong>、<strong>负载均衡</strong>、<strong>集群管理</strong>等。</p></li><li><p>ZooKeeper 提供基于类似于 Linux 文件系统的目录节点树方式的数据存储，即分层命名空间。Zookeeper 并不是用来专门存储数据的，它的作用主要是用来维护和监控你存储的数据的状态变化，通过监控这些数据状态的变化，从而可以达到基于数据的集群管理，ZooKeeper 节点的数据上限是 1MB。</p></li><li><p>我们可以认为 <strong>Zookeeper = 文件系统 + 通知机制</strong>，对于 ZooKeeper 的数据结构，每个子目录项如 NameService 都被称作为 znode，这个 znode 是被它所在的路径唯一标识，如 Server1 这个 znode 的标识为 <code>/NameService/Server1</code>；</p></li><li><p>znode 可以有子节点目录，并且每个 znode 可以存储数据，注意 <code>EPHEMERAL</code> 类型的目录节点不能有子节点目录（因为它是临时节点）；</p></li><li><p>znode 是有版本的，每个 znode 中存储的数据可以有多个版本，也就是一个访问路径中可以存储多份数据；</p></li><li><p>znode 可以是临时节点，一旦创建这个 znode 的客户端与服务器失去联系，这个 znode 也将自动删除，Zookeeper 的客户端和服务器通信采用长连接方式，每个客户端和服务器通过心跳来保持连接，这个连接状态称为 session，如果 znode 是临时节点，这个 session 失效，znode 也就删除了；</p></li><li><p>znode 的目录名可以自动编号，如 App1 已经存在，再创建的话，将会自动命名为 App2；</p></li><li><p>znode 可以被监控，包括这个目录节点中存储的数据的修改，子节点目录的变化等，一旦变化可以通知设置监控的客户端，这个是 Zookeeper 的核心特性，Zookeeper 的很多功能都是基于这个特性实现的，后面在典型的应用场景中会有实例介绍。</p></li></ul><h2 id="ZooKeeper-节点类型？"><a href="#ZooKeeper-节点类型？" class="headerlink" title="ZooKeeper 节点类型？"></a>ZooKeeper 节点类型？</h2><ol><li>znode 有两种类型：<ul><li>短暂（ephemeral）：客户端和服务器端断开连接后，创建的节点自己删除。</li><li>持久（persistent）：客户端和服务器端断开连接后，创建的节点不删除。</li></ul></li><li>znode 有四种形式的目录节点（默认是 persistent）<ul><li>持久化目录节点（PERSISTENT）客户端与 Zookeeper 断开连接后，该节点依旧存在。</li><li>持久化顺序编号目录节点（PERSISTENT_SEQUENTIAL） 客户端与 Zookeeper 断开连接后，该节点依旧存在，只是 Zookeeper 给该节点名称进行顺序编号。</li><li>临时目录节点（EPHEMERAL） 客户端与 Zookeeper 断开连接后，该节点被删除。</li><li>临时顺序编号目录节点（EPHEMERAL_SEQUENTIAL）客户端与 Zookeeper 断开连接后，该节点被删除，只是 Zookeeper 给该节点名称进行顺序编号。</li></ul></li></ol><h2 id="请说明-ZooKeeper-的通知机制？"><a href="#请说明-ZooKeeper-的通知机制？" class="headerlink" title="请说明 ZooKeeper 的通知机制？"></a>请说明 ZooKeeper 的通知机制？</h2><p>ZooKeeper 选择了基于通知（notification）的机制，即：客户端向 ZooKeeper 注册需要接受通知的 znode，通过 znode 设置监控点（watch）来接受通知。监视点是一个单次触发的操作，意即监视点会触发一个通知。为了接收多个通知，客户端必须在每次通知后设置一个新的监视点。在下图阐述的情况下，当节点 <code>/task</code> 发生变化时，客户端会受到一个通知，并从 ZooKeeper 读取一个新值。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/zookeeper_notification.png" alt="ZooKeeper 通知机制"></p><h2 id="ZooKeeper-的监听原理是什么？"><a href="#ZooKeeper-的监听原理是什么？" class="headerlink" title="ZooKeeper 的监听原理是什么？"></a>ZooKeeper 的监听原理是什么？</h2><p>在应用程序中，<code>main()</code> 方法首先会创建 zkClient，创建 zkClient 的同时就会产生两个进程，即 <code>Listener</code> 进程（监听进程）和 <code>connect</code> 进程（网络连接/传输进程），当 zkClient 调用 <code>getChildren()</code> 等方法注册监视器时，<code>connect</code> 进程向 ZooKeeper 注册监听器，注册后的监听器位于 ZooKeeper 的监听器列表中，监听器列表中记录了 zkClient 的 IP，端口号以及要监控的路径，一旦目标文件发生变化，ZooKeeper 就会把这条消息发送给对应的 zkClient 的 <code>Listener()</code> 进程，<code>Listener</code> 进程接收到后，就会执行 <code>process()</code> 方法，在 <code>process()</code> 方法中针对发生的事件进行处理。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/zookeeper_listener.png" alt="ZooKeeper 监听原理"></p><h2 id="请说明-ZooKeeper-使用到的各个端口的作用？"><a href="#请说明-ZooKeeper-使用到的各个端口的作用？" class="headerlink" title="请说明 ZooKeeper 使用到的各个端口的作用？"></a>请说明 ZooKeeper 使用到的各个端口的作用？</h2><ul><li><code>2888</code>：Follower 与 Leader 交换信息的端口。</li><li><code>3888</code>：万一集群中的 Leader 服务器挂了，需要一个端口来重新进行选举，选出一个新的 Leader，而这个端口就是用来执行选举时服务器相互通信的端口。</li></ul><h2 id="ZooKeeper-的部署方式有哪几种？集群中的角色有哪些？集群最少需要几台机器？"><a href="#ZooKeeper-的部署方式有哪几种？集群中的角色有哪些？集群最少需要几台机器？" class="headerlink" title="ZooKeeper 的部署方式有哪几种？集群中的角色有哪些？集群最少需要几台机器？"></a>ZooKeeper 的部署方式有哪几种？集群中的角色有哪些？集群最少需要几台机器？</h2><p>ZooKeeper 的部署方式有<strong>单机模式</strong>和<strong>集群模式</strong>，集群中的角色有 Leader 和 Follower，集群最少 3（2N+1）台，根据选举算法，应保证奇数。</p><h2 id="ZooKeeper-集群如果有-3-台机器，挂掉一台是否还能工作？挂掉两台呢？"><a href="#ZooKeeper-集群如果有-3-台机器，挂掉一台是否还能工作？挂掉两台呢？" class="headerlink" title="ZooKeeper 集群如果有 3 台机器，挂掉一台是否还能工作？挂掉两台呢？"></a>ZooKeeper 集群如果有 3 台机器，挂掉一台是否还能工作？挂掉两台呢？</h2><p>对于 ZooKeeper 集群，过半存活即可使用。</p><h2 id="ZooKeeper-使用的-ZAB-协议与-Paxo-算法的异同？"><a href="#ZooKeeper-使用的-ZAB-协议与-Paxo-算法的异同？" class="headerlink" title="ZooKeeper 使用的 ZAB 协议与 Paxo 算法的异同？"></a>ZooKeeper 使用的 ZAB 协议与 Paxo 算法的异同？</h2><p>Paxos 算法是分布式选举算法，Zookeeper 使用的 ZAB 协议（Zookeeper 原子广播），两者的异同如下：</p><ul><li><p>相同之处：</p><p>比如都有一个 Leader，用来协调 N 个 Follower 的运行；Leader 要等待超半数的 Follower 做出正确反馈之后才进行提案；二者都有一个值来代表 Leader 的周期。</p></li><li><p>不同之处：</p><p>ZAB 用来构建高可用的分布式数据主备系统（Zookeeper），Paxos 是用来构建分布式一致性状态机系统。</p></li></ul><h2 id="请谈谈对-ZooKeeper-对事务性的支持？"><a href="#请谈谈对-ZooKeeper-对事务性的支持？" class="headerlink" title="请谈谈对 ZooKeeper 对事务性的支持？"></a>请谈谈对 ZooKeeper 对事务性的支持？</h2><p>ZooKeeper 对于事务性的支持主要依赖于四个函数，<code>zoo_create_op_init</code>，<code>zoo_delete_op_init</code>，<code>zoo_set_op_init</code> 以及 <code>zoo_check_op_init</code>。</p><p>每一个函数都会在客户端初始化一个 <code>operation</code>，客户端程序有义务保留这些<code>operations</code>。当准备好一个事务中的所有操作后，可以使用 <code>zoo_multi</code> 来提交所有的操作，由 Zookeeper 服务来保证这一系列操作的原子性。也就是说只要其中有一个操作失败了，相当于此次提交的任何一个操作都没有对服务端的数据造成影响。<code>zoo_multi</code> 的返回值是第一个失败操作的状态信号。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Zookeeper/">Zookeeper</category>
      
      
      <comments>https://blog.eurkon.com/post/2f1ea7f2.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Hadoop 面试题解析</title>
      <link>https://blog.eurkon.com/post/2b822834.html</link>
      <guid>https://blog.eurkon.com/post/2b822834.html</guid>
      <pubDate>Fri, 26 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Hadoop-基础&quot;&gt;&lt;a href=&quot;#Hadoop-基础&quot; class=&quot;headerlink&quot; title=&quot;Hadoop 基础&quot;&gt;&lt;/a&gt;Hadoop 基础&lt;/h2&gt;&lt;h3 id=&quot;集群的最主要瓶颈？&quot;&gt;&lt;a href=&quot;#集群的最主要瓶颈？&quot; class</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Hadoop-基础"><a href="#Hadoop-基础" class="headerlink" title="Hadoop 基础"></a>Hadoop 基础</h2><h3 id="集群的最主要瓶颈？"><a href="#集群的最主要瓶颈？" class="headerlink" title="集群的最主要瓶颈？"></a>集群的最主要瓶颈？</h3><p>磁盘 IO。</p><h3 id="Hadoop-运行模式？"><a href="#Hadoop-运行模式？" class="headerlink" title="Hadoop 运行模式？"></a>Hadoop 运行模式？</h3><ul><li>独立（本地）运行模式：无需任何守护进程，所有的程序都运行在同一个 JVM 上执行。在独立模式下调试 MR 程序非常高效方便。所以一般该模式主要是在学习或者开发阶段调试使用。</li><li>伪分布式模式：Hadoop 守护进程运行在本地机器上，模拟一个小规模的集群，换句话说，可以配置一台机器的 Hadoop 集群，伪分布式是完全分布式的一个特例。</li><li>完全分布式模式：Hadoop 守护进程运行在一个集群上。</li></ul><p><strong>注意：</strong>所谓分布式要启动守护进程，即：使用分布式 Hadoop 时，要先启动一些准备程序进程，然后才能使用比如 <code>start-dfs.sh</code>、<code>start-yarn.sh</code>。而本地模式不需要启动这些守护进程。</p><p>三种模式的集群必须配置信息：</p><p>下面详细分析配置三种模式的“集群”所需要的必须配置。可以配置完，体验一把，就可以主观地感受三种之间的区别。</p><div class="table-container"><table><thead><tr><th>组件名称</th><th>属性名称</th><th>本地模式</th><th>伪分布式</th><th>完全分布式</th></tr></thead><tbody><tr><td>Common</td><td>fs.defaultFs</td><td>file:///(默认)</td><td>hdfs://localhost/</td><td>hdfs://namenode</td></tr><tr><td>HDFS</td><td>dfs.replication</td><td>N/A</td><td>1</td><td>3(默认)</td></tr><tr><td>MapReduce</td><td>mapreduce.framework.name</td><td>local(默认)</td><td>yarn</td><td>yarn</td></tr><tr><td>Yarn</td><td>yarn.resoucemanager.hostname <br> yarn.nodemanager.auxservice</td><td>N/A <br> N/A</td><td>localhost <br> mapreduce_shuffle</td><td>resoucemanager <br> maperduce_shuffle</td></tr></tbody></table></div><p><strong>注意：</strong>在本地模式下，将使用本地文件系统和本地 MapReduce 运行器。在分布式模式下，将启动 HDFS 和 YARN 守护进程。</p><h3 id="Hadoop-生态圈的组件并做简要描述？"><a href="#Hadoop-生态圈的组件并做简要描述？" class="headerlink" title="Hadoop 生态圈的组件并做简要描述？"></a>Hadoop 生态圈的组件并做简要描述？</h3><ul><li>Zookeeper：是一个开源的分布式应用程序协调服务，基于 Zookeeper 可以实现同步服务，配置维护，命名服务。</li><li>Flume：一个高可用的，高可靠的，分布式的海量日志采集、聚合和传输的系统。</li><li>Hbase：是一个分布式的、面向列的开源数据库，利用 Hadoop HDFS 作为其存储系统。</li><li>Hive：基于 Hadoop 的一个数据仓库工具，可以将结构化的数据档映射为一张数据库表，并提供简单的 SQL 查询功能，可以将 SQL 语句转换为 MapReduce 任务进行运行。</li><li>Sqoop：将一个关系型数据库中的数据导进到 Hadoop 的 HDFS 中，也可以将 HDFS 的数据导进到关系型数据库中。</li></ul><h3 id="解释“Hadoop”和“Hadoop-生态系统”两个概念？"><a href="#解释“Hadoop”和“Hadoop-生态系统”两个概念？" class="headerlink" title="解释“Hadoop”和“Hadoop 生态系统”两个概念？"></a>解释“Hadoop”和“Hadoop 生态系统”两个概念？</h3><p>Hadoop 是指 Hadoop 框架本身；Hadoop 生态系统，不仅包含 Hadoop，还包括保证 Hadoop 框架正常高效运行其他框架，比如 Zookeeper、Flume、Hbase、Hive、Sqoop 等辅助框架。</p><h3 id="请列出正常工作的-Hadoop-集群中-Hadoop-都分别需要启动哪些进程，它们的作用分别是什么？"><a href="#请列出正常工作的-Hadoop-集群中-Hadoop-都分别需要启动哪些进程，它们的作用分别是什么？" class="headerlink" title="请列出正常工作的 Hadoop 集群中 Hadoop 都分别需要启动哪些进程，它们的作用分别是什么？"></a>请列出正常工作的 Hadoop 集群中 Hadoop 都分别需要启动哪些进程，它们的作用分别是什么？</h3><ul><li>NameNode：它是 Hadoop 中的主服务器，管理文件系统名称空间和对集群中存储的文件的访问，保存有 metadate。</li><li>SecondaryNameNode：它不是 NameNode 的冗余守护进程，而是提供周期检查点和清理任务。帮助 NameNode 合并 editslog，减少 NameNode 启动时间。</li><li>DataNode：它负责管理连接到节点的存储（一个集群中可以有多个节点）。每个存储数据的节点运行一个 DataNode 守护进程。</li><li>ResourceManager（JobTracker）：JobTracker 负责调度 DataNode 上的工作。每个 DataNode 有一个 TaskTracker，它们执行实际工作。</li><li>NodeManager（TaskTracker）：执行任务。</li><li>DFSZKFailoverController：高可用时它负责监控 NameNode 的状态，并及时的把状态信息写入 Zookeeper。它通过一个独立线程周期性的调用 NameNode 上的一个特定接口来获取 NameNode 的健康状态。FC 也有选择谁作为 Active NameNode 的权利，因为最多只有两个节点，目前选择策略还比较简单（先到先得，轮换）。</li><li>JournalNode：高可用情况下存放 NameNode 的 EditLog 文件。</li></ul><h3 id="NameNode-在启动的时候会做哪些操作？"><a href="#NameNode-在启动的时候会做哪些操作？" class="headerlink" title="NameNode 在启动的时候会做哪些操作？"></a>NameNode 在启动的时候会做哪些操作？</h3><p>NameNode 数据存储在内存和本地磁盘，本地磁盘数据存储在 fsimage 镜像文件和 edits 编辑日志文件。</p><ul><li><p>首次启动 NameNode</p><ol><li>格式化文件系统，为了生成 fsimage 镜像文件；</li><li>启动 NameNode；<ol><li>读取 fsimage 文件，将文件内容加载进内存；</li><li>等待 DataNode 注册与发送 block report。</li></ol></li><li>启动 DataNode；<ol><li>向 NameNode 注册；</li><li>发送 block report；</li><li>检查 fsimage 中记录的块的数量和 block report 中的块的总数是否相同。</li></ol></li><li>对文件系统进行操作（创建目录，上传文件，删除文件等）。<ol><li>此时内存中已经有文件系统改变的信息，但是磁盘中没有文件系统改变的信息，此时会将这些改变信息写入 edits 文件中，edits 文件中存储的是文件系统元数据改变的信息。</li></ol></li></ol></li><li><p>第二次启动 NameNode</p><ol><li>读取 fsimage 和 edits 文件；</li><li>将 fsimage 和 edits 文件合并成新的 fsimage 文件；</li><li>创建新的 edits 文件，内容为空；</li><li>启动 DataNode。</li></ol></li></ul><h3 id="DataNode-了解吗，它的工作机制是怎样的？"><a href="#DataNode-了解吗，它的工作机制是怎样的？" class="headerlink" title="DataNode 了解吗，它的工作机制是怎样的？"></a>DataNode 了解吗，它的工作机制是怎样的？</h3><ol><li>一个数据块在 DataNode 上以文件形式存储在磁盘上，包括两个文件，一个是数据本身，一个是元数据包括数据块的长度，块数据的校验和，时间戳。</li><li>DataNode 启动后向 NameNode 进行注册，通过后，周期性（1 小时）地向 NameNode 上报所有的块信息；</li><li>心跳每 3 秒一次，心跳返回结果带有 NameNode 给该 DataNode 的命令，如复制块数据到另一台机器，或删除某个数据块。如果超过 10 分钟没有收到某个 DataNode 的心跳，则认为该节点不可用；</li><li>集群运行中可以安全加入和退出一些机器。</li></ol><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/datanode.png" alt="DataNode"></p><h3 id="Secondary-NameNode-了解吗，它的工作机制是怎样的？"><a href="#Secondary-NameNode-了解吗，它的工作机制是怎样的？" class="headerlink" title="Secondary NameNode 了解吗，它的工作机制是怎样的？"></a>Secondary NameNode 了解吗，它的工作机制是怎样的？</h3><p>Secondary NameNode 目的是合并 NameNode 的 edit logs 到 fsimage 文件中，是减少 NameNode 启动时间，它的具体工作机制：</p><ol><li>第一阶段：NameNode 启动<ol><li>第一次启动 NameNode 格式化后，创建 fsimage 和 edits 文件。如果不是第一次启动，直接加载编辑日志和镜像文件到内存；</li><li>客户端对元数据进行增删改的请求；</li><li>NameNode 记录操作日志，更新滚动日志；</li><li>NameNode 在内存中对数据进行增删改查。</li></ol></li><li>第二阶段：Secondary NameNode 工作<ol><li>Secondary NameNode 询问 NameNode 是否需要 checkpoint。直接带回 NameNode 是否检查结果；</li><li>Secondary NameNode 请求执行 checkpoint；</li><li>NameNode 滚动正在写的 edits 日志</li><li>将滚动前的编辑日志和镜像文件拷贝到 Secondary NameNode；</li><li>Secondary NameNode 加载编辑日志和镜像文件到内存，并合并；</li><li>生成新的镜像文件 fsimage.chkpoint；</li><li>拷贝 fsimage.chkpoint 到 NameNode；</li><li>NameNode 将 fsimage.chkpoint 重新命名成 fsimage 所以如果 NameNode 中的元数据丢失，是可以从 Secondary NameNode 恢复一部分元数据信息的，但不是全部，因为 NameNode 正在写的 edits 日志还没有拷贝到 Secondary NameNode，这部分恢复不了。</li></ol></li></ol><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/namenode.png" alt="NameNode"></p><h3 id="Secondary-NameNode-不能恢复-NameNode-的全部数据，那如何保证-NameNode-数据存储安全？"><a href="#Secondary-NameNode-不能恢复-NameNode-的全部数据，那如何保证-NameNode-数据存储安全？" class="headerlink" title="Secondary NameNode 不能恢复 NameNode 的全部数据，那如何保证 NameNode 数据存储安全？"></a>Secondary NameNode 不能恢复 NameNode 的全部数据，那如何保证 NameNode 数据存储安全？</h3><p>这个问题就要说 NameNode 的高可用了，即 <strong>NameNode HA</strong> 一个 NameNode 有单点故障的问题，那就配置双 NameNode，配置有两个关键点，一是必须要保证这两个 NameNode 的元数据信息必须要同步的，二是一个 NameNode 挂掉之后另一个要立马补上。</p><ul><li><p>元数据信息同步在 HA 方案中采用的是“共享存储”。每次写文件时，需要将日志同步写入共享存储，这个步骤成功才能认定写文件成功。然后备份节点定期从共享存储同步日志，以便进行主备切换。</p></li><li><p>监控 NameNode 状态采用 Zookeeper，两个 NameNode 节点的状态存放在 Zookeeper 中，另外两个 NameNode 节点分别有一个进程监控程序，实施读取 Zookeeper 中有 NameNode 的状态，来判断当前的 NameNode 是不是已经 down 机。如果 standby 的 NameNode 节点的 ZKFC 发现主节点已经挂掉，那么就会强制给原本的 active  NameNode 节点发送强制关闭请求，之后将备用的 NameNode 设置为 active。</p></li></ul><p><strong>如果面试官再问 HA 中的共享存储是怎么实现的知道吗？</strong></p><p>可以进行解释下：NameNode 共享存储方案有很多，比如 Linux HA，VMware FT，QJM 等，目前社区已经把由 Cloudera 公司实现的基于 QJM（Quorum Journal Manager）的方案合并到 HDFS 的 trunk 之中并且作为<strong>默认的共享存储</strong>实现基于 QJM 的共享存储系统主要用于保存 EditLog，并不保存 FSImage 文件。FSImage 文件还是在 NameNode 的本地磁盘上。</p><p>QJM 共享存储的基本思想来自于 Paxos 算法，采用多个称为 JournalNode 的节点组成的 JournalNode 集群来存储 EditLog。每个 JournalNode 保存同样的 EditLog 副本。每次 NameNode 写 EditLog 的时候，除了向本地磁盘写入 EditLog 之外，也会并行地向 JournalNode 集群之中的每一个 JournalNode 发送写请求，只要大多数（majority）的 JournalNode 节点返回成功就认为向 JournalNode 集群写入 EditLog 成功。如果有 2N+1 台 JournalNode，那么根据大多数的原则，最多可以容忍有 N 台 JournalNode 节点挂掉。</p><h3 id="在-NameNode-HA-中，会出现脑裂问题吗？怎么解决脑裂？"><a href="#在-NameNode-HA-中，会出现脑裂问题吗？怎么解决脑裂？" class="headerlink" title="在 NameNode HA 中，会出现脑裂问题吗？怎么解决脑裂？"></a>在 NameNode HA 中，会出现脑裂问题吗？怎么解决脑裂？</h3><p>假设 NameNode1 当前为 Active 状态，NameNode2 当前为 Standby 状态。如果某一时刻 NameNode1 对应的 ZKFailoverController 进程发生了“假死”现象，那么 Zookeeper 服务端会认为 NameNode1 挂掉了，根据前面的主备切换逻辑，NameNode2 会替代 NameNode1 进入 Active 状态。但是此时 NameNode1 可能仍然处于 Active 状态正常运行，这样 NameNode1 和 NameNode2 都处于 Active 状态，都可以对外提供服务。这种情况称为脑裂。</p><p>脑裂对于 NameNode 这类对数据一致性要求非常高的系统来说是灾难性的，数据会发生错乱且无法恢复。Zookeeper 社区对这种问题的解决方法叫做 fencing，中文翻译为隔离，也就是想办法把旧的 Active NameNode 隔离起来，使它不能正常对外提供服务。在进行 fencing 的时候，会执行以下的操作：</p><ol><li>首先尝试调用这个旧 Active NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法，看能不能把它转换为 Standby 状态。</li><li>如果 transitionToStandby 方法调用失败，那么就执行 Hadoop 配置文件之中预定义的隔离措施，Hadoop 目前主要提供两种隔离措施，通常会选择 sshfence：<ul><li>sshfence：通过 SSH 登录到目标机器上，执行命令 fuser 将对应的进程杀死；</li><li>shellfence：执行一个用户自定义的 shell 脚本来将对应的进程隔离。</li></ul></li></ol><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/ha_namenode.png" alt="HA NameNode"></p><p><strong>补充：ZKFailoverController 主要职责</strong></p><ul><li>健康监测：周期性的向它监控的 NameNode 发送健康探测命令，从而来确定某个 NameNode 是否处于健康状态，如果机器宕机，心跳失败，那么 ZKFC 就会标记它处于一个不健康的状态；</li><li>会话管理：如果 NameNode 是健康的，ZKFC 就会在 Zookeeper 中保持一个打开的会话，如果 NameNode 同时还是 Active 状态的，那么 ZKFC 还会在 Zookeeper 中占有一个类型为短暂类型的 znode，当这个 NameNode 挂掉时，这个 znode 将会被删除，然后备用的 NameNode ，将会得到这把锁，升级为主 NameNode ，同时标记状态为 Active；</li><li>当宕机的 NameNode 新启动时，它会再次注册 Zookeper，发现已经有 znode 锁了，便会自动变为 Standby 状态，如此往复循环，保证高可靠，需要注意，目前仅仅支持最多配置 2 个 NameNode；</li><li>master 选举：如上所述，通过在 Zookeeper 中维持一个短暂类型的 znode，来实现抢占式的锁机制，从而判断那个 NameNode 为 Active 状态。</li></ul><h2 id="Hadoop-HDFS"><a href="#Hadoop-HDFS" class="headerlink" title="Hadoop HDFS"></a>Hadoop HDFS</h2><h3 id="HDFS-中的-block-默认保存几份？"><a href="#HDFS-中的-block-默认保存几份？" class="headerlink" title="HDFS 中的 block 默认保存几份？"></a>HDFS 中的 block 默认保存几份？</h3><p>默认保存 3 份。</p><h3 id="HDFS-默认-BlockSize-是多大？"><a href="#HDFS-默认-BlockSize-是多大？" class="headerlink" title="HDFS 默认 BlockSize 是多大？"></a>HDFS 默认 BlockSize 是多大？</h3><p>Hadoop1.x 是 64MB，hadoop2.x 开始是 128MB。</p><h3 id="负责-HDFS-数据存储的是哪一部分？"><a href="#负责-HDFS-数据存储的是哪一部分？" class="headerlink" title="负责 HDFS 数据存储的是哪一部分？"></a>负责 HDFS 数据存储的是哪一部分？</h3><p>DataNode 负责数据存储</p><h3 id="HDFS-组成架构？"><a href="#HDFS-组成架构？" class="headerlink" title="HDFS 组成架构？"></a>HDFS 组成架构？</h3><p>架构主要由四个部分组成，分别为 HDFS Client、NameNode、DataNode 和 Secondary NameNode。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_architecture.png" alt="HDFS 架构"></p><ul><li>Client：就是客户端。<ul><li>文件切分。文件上传 HDFS 的时候，Client 将文件切分成一个一个的 Block，然后进行存储；</li><li>与 NameNode 交互，获取文件的位置信息；</li><li>与 DataNode 交互，读取或者写入数据；</li><li>Client 提供一些命令来管理 HDFS，比如启动或者关闭 HDFS；</li><li>Client 可以通过一些命令来访问 HDFS。</li></ul></li><li>NameNode：就是 Master，它是一个主管、管理者。<ul><li>管理 HDFS 的名称空间；</li><li>管理数据块（Block）映射信息；</li><li>配置副本策略；</li><li>处理客户端读写请求。</li></ul></li><li>DataNode：就是 Slave。NameNode 下达命令，DataNode 执行实际的操作。<ul><li>存储实际的数据块；</li><li>执行数据块的读/写操作。</li></ul></li><li>Secondary NameNode：并非 NameNode 的热备。当 NameNode 挂掉的时候，它并不能马上替换 NameNode 并提供服务。<ul><li>辅助 NameNode，分担其工作量；</li><li>定期合并 fsimage 和 edits，并推送给 NameNode；</li><li>在紧急情况下，可辅助恢复 NameNode。</li></ul></li></ul><h3 id="文件大小设置，增大有什么影响？"><a href="#文件大小设置，增大有什么影响？" class="headerlink" title="文件大小设置，增大有什么影响？"></a>文件大小设置，增大有什么影响？</h3><p>HDFS 中的文件在物理上是分块存储（block），块的大小可以通过配置参数 <code>dfs.blocksize</code> 来规定，默认大小在 hadoop2.x 版本中是 128MB，老版本中是 64MB。</p><p><strong>思考：为什么块的大小不能设置的太小，也不能设置的太大？</strong></p><p>HDFS 的块比磁盘的块大，其目的是为了最小化寻址开销。如果块设置得足够大，从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。因而，传输一个由多个块组成的文件的时间取决于磁盘传输速率。</p><p>如果寻址时间约为 10ms，而传输速率为 100MB/s，为了使寻址时间仅占传输时间的 1%，我们要将块大小设置约为 10ms×100×100M/s = 100MB。如图：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/blocksize.png" alt="Block Size"></p><p>默认的块大小 128MB，增加文件块大小，需要增加磁盘的传输速率。</p><h3 id="HDFS-的压缩算法？"><a href="#HDFS-的压缩算法？" class="headerlink" title="HDFS 的压缩算法？"></a>HDFS 的压缩算法？</h3><p>企业开发用的比较多的是 snappy。</p><div class="table-container"><table><thead><tr><th>压缩算法</th><th>优点</th><th>缺点</th><th>应用场景</th></tr></thead><tbody><tr><td>Gzip</td><td>- 压缩比例比较高，而且压缩、解压速度比较快； <br> - hadoop 本身支持，在应用中处理 gzip 格式的文件就和直接处理文本一样； <br> - 大部分 linux 系统都自带 gzip 命令，使用方便。</td><td>不支持 split。</td><td>当每个文件压缩之后在 130M 以内的（1 个块大小内），都可以考虑用 gzip 压缩格式。</td></tr><tr><td>Bzip2</td><td>- 支持 split； <br> - 具有很高的压缩率，比 gzip 压缩率都高； <br> - hadoop 本身支持，但不支持 native； <br> - 在 linux 系统下自带 bzip2 命令，使用方便。</td><td>- 压缩/解压速度慢； <br> - 不支持 native。</td><td>- 适合对速度要求不高，但需要较高的压缩率的时候，可以作为 MapReduce 作业的输出格式； <br> - 或者输出之后的数据比较大，处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情况； <br> - 或者对单个很大的文本文件想压缩减少存储空间，同时又需要支持 split，而且兼容之前的应用程序（即应用程序不需要修改）的情况。</td></tr><tr><td>Lzo</td><td>- 压缩/解压速度也比较快，合理的压缩率； <br> - 支持 split，是 hadoop 中最流行的压缩格式； <br> - 可以在 linux 系统下安装 lzop 命令，使用方便。</td><td>- 压缩率比 gzip 要低一些； <br> - hadoop 本身不支持，需要安装； <br> - 在应用中对 lzo 格式的文件需要做一些特殊处理（为了支持 split 需要建索引，还需要指定 inputFormat 为 lzo 格式）。</td><td>一个很大的文本文件，压缩之后还大于 200MB 以上的可以考虑，而且单个文件越大，lzo 优点越越明显。</td></tr><tr><td>Snappy</td><td>高速压缩速度和合理的压缩率。</td><td>- 不支持 split； <br> - 压缩率比 gzip 要低； <br> - hadoop 本身不支持，需要安装。</td><td>- 当 MapReduce 作业的 Map 输出的数据比较大的时候，作为 Map 到 Reduce 的中间数据的压缩格式； <br> - 或者作为一个 MapReduce 作业的输出和另外一个 MapReduce 作业的输入。</td></tr></tbody></table></div><h3 id="HDFS-的存储机制？"><a href="#HDFS-的存储机制？" class="headerlink" title="HDFS 的存储机制？"></a>HDFS 的存储机制？</h3><p>HDFS 存储机制，包括 HDFS 的<strong>写入数据</strong>过程和<strong>读取数据</strong>过程两部分</p><p><strong>HDFS 写数据过程</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_write.png" alt="HDFS 写数据"></p><ol><li>客户端通过 Distributed FileSystem 模块向 NameNode 请求上传文件，NameNode 检查目标文件是否已存在，父目录是否存在；</li><li>NameNode 返回是否可以上传；</li><li>客户端请求第一个 block 上传到哪几个 DataNode 服务器上；</li><li>NameNode 返回 3 个 DataNode 节点，分别为 dn1、dn2、dn3；</li><li>客户端通过 FSDataOutputStream 模块请求 dn1 上传数据，dn1 收到请求会继续调用 dn2，然后 dn2 调用 dn3，将这个<strong>通信管道</strong>建立完成；</li><li>dn1、dn2、dn3 逐级应答客户端；</li><li>客户端开始往 dn1 上传第一个 block（先从磁盘读取数据放到一个本地内存缓存），以 packet 为单位，dn1 收到一个 packet 就会传给 dn2，dn2 传给 dn3；dn1 每传一个 packet 会放入一个应答队列等待应答；</li><li>当一个 block 传输完成之后，客户端再次请求 NameNode 上传第二个 block 的服务器。（重复执行 3~7 步）。</li></ol><p><strong>HDFS 读数据过程</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_read.png" alt="HDFS 读数据"></p><ol><li>客户端通过 Distributed FileSystem 向 NameNode 请求下载文件，NameNode 通过查询元数据，找到文件块所在的 DataNode 地址；</li><li>挑选一台 DataNode（就近原则，然后随机）服务器，请求读取数据；</li><li>DataNode 开始传输数据给客户端（从磁盘里面读取数据输入流，以 packet 为单位来做校验）；</li><li>客户端以 packet 为单位接收，先在本地缓存，然后写入目标文件。</li></ol><p>可以参考下面的漫画进行理解：</p><ul><li><strong>HDFS 写数据</strong></li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_write1.png" alt="HDFS 写数据1"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_write2.png" alt="HDFS 写数据2"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_write3.png" alt="HDFS 写数据3"></p><ul><li><strong>HDFS 读数据</strong></li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_read1.png" alt="HDFS 读数据"></p><ul><li><strong>容错</strong></li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_error1.png" alt="HDFS 容错1"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_error2.png" alt="HDFS 容错2"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_error3.png" alt="HDFS 容错3"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_error4.png" alt="HDFS 容错4"></p><ul><li><strong>副本存放策略</strong></li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/hdfs_duplication.png" alt="副本存放策略"></p><h2 id="Hadoop-MapReduce"><a href="#Hadoop-MapReduce" class="headerlink" title="Hadoop MapReduce"></a>Hadoop MapReduce</h2><h3 id="谈谈-Hadoop-序列化和反序列化及自定义-bean-对象实现序列化？"><a href="#谈谈-Hadoop-序列化和反序列化及自定义-bean-对象实现序列化？" class="headerlink" title="谈谈 Hadoop 序列化和反序列化及自定义 bean 对象实现序列化？"></a>谈谈 Hadoop 序列化和反序列化及自定义 bean 对象实现序列化？</h3><ul><li><p>序列化和反序列化</p><ul><li>序列化就是把内存中的对象，转换成字节序列（或其他数据传输协议）以便于存储（持久化）和网络传输。</li><li>反序列化就是将收到字节序列（或其他数据传输协议）或者是硬盘的持久化数据，转换成内存中的对象。</li><li>Java 的序列化是一个重量级序列化框架（Serializable），一个对象被序列化后，会附带很多额外的信息（各种校验信息，header，继承体系等），不便于在网络中高效传输。所以，Hadoop 自己开发了一套序列化机制（Writable），精简、高效。</li></ul></li><li><p>自定义 bean 对象要想序列化传输步骤及注意事项：</p><ol><li>必须实现 Writable 接口；</li><li>反序列化时，需要反射调用空参构造函数，所以必须有空参构造；</li><li>重写序列化方法；</li><li>重写反序列化方法；</li><li>注意反序列化的顺序和序列化的顺序完全一致；</li><li>要想把结果显示在文件中，需要重写 <code>toString()</code>，且用 <code>\t</code> 分开，方便后续用；</li><li>如果需要将自定义的 bean 放在 key 中传输，则还需要实现 comparable 接口，因为 mapreduce 框中的 shuffle 过程一定会对 key 进行排序。</li></ol></li></ul><h3 id="MapTask-和-ReduceTask-工作机制（MapReduce-工作原理）？"><a href="#MapTask-和-ReduceTask-工作机制（MapReduce-工作原理）？" class="headerlink" title="MapTask 和 ReduceTask 工作机制（MapReduce 工作原理）？"></a>MapTask 和 ReduceTask 工作机制（MapReduce 工作原理）？</h3><p><strong>MapTask 工作机制</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/map_task.png" alt="MapTask 工作机制"></p><ol><li>Read 阶段：Map Task 通过用户编写的 RecordReader，从输入 InputSplit 中解析出一个个 key/value；</li><li>Map 阶段：该节点主要是将解析出的 key/value 交给用户编写 <code>map()</code> 函数处理，并产生一系列新的 key/value；</li><li>Collect 收集阶段：在用户编写 <code>map()</code> 函数中，当数据处理完成后，一般会调用 <code>OutputCollector.collect()</code> 输出结果。在该函数内部，它会将生成的 key/value 分区（调用 Partitioner），并写入一个环形内存缓冲区中；</li><li>Spill 阶段：即“溢写”，当环形缓冲区满后，MapReduce 会将数据写到本地磁盘上，生成一个临时文件。需要注意的是，将数据写入本地磁盘之前，先要对数据进行一次本地排序，并在必要时对数据进行合并、压缩等操作；</li><li>Combine 阶段：当所有数据处理完成后，MapTask 对所有临时文件进行一次合并，以确保最终只会生成一个数据文件。</li></ol><p><strong>ReduceTask 工作机制</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/reduce_task.png" alt="ReduceTask 工作机制"></p><ol><li>Copy 阶段：ReduceTask 从各个 MapTask 上远程拷贝一片数据，并针对某一片数据，如果其大小超过一定阈值，则写到磁盘上，否则直接放到内存中；</li><li>Merge 阶段：在远程拷贝数据的同时，ReduceTask 启动了两个后台线程对内存和磁盘上的文件进行合并，以防止内存使用过多或磁盘上文件过多；</li><li>Sort 阶段：按照 MapReduce 语义，用户编写 <code>reduce()</code> 函数输入数据是按 key 进行聚集的一组数据。为了将 key 相同的数据聚在一起，Hadoop 采用了基于排序的策略。由于各个 MapTask 已经实现对自己的处理结果进行了局部排序，因此， ReduceTask 只需对所有数据进行一次归并排序即可；</li><li>Reduce 阶段：<code>reduce()</code> 函数将计算结果写到 HDFS 上。</li></ol><h3 id="如何判定一个-job-的-map-和-reduce-的数量"><a href="#如何判定一个-job-的-map-和-reduce-的数量" class="headerlink" title="如何判定一个 job 的 map 和 reduce 的数量?"></a>如何判定一个 job 的 map 和 reduce 的数量?</h3><ul><li><p><strong>map 的数量</strong></p><p>  map 个数的计算公式为：<code>splitSize = max(minSize, min(maxSize, blockSize))</code>，如果没有设置 minSize 和 maxSize，splitSize 的大小默认等于 blockSize，</p><p>  在 map 阶段读取数据前，FileInputFormat 会将输入文件分割成 split。split 的个数决定了 map 的个数。影响 map 个数，即 split 个数的因素主要有：</p><ul><li><p>HDFS 块的大小，即 HDFS 中 <code>dfs.block.size</code> 的值。如果有一个输入文件为 1024MB，当块为 256MB 时，会被划分为 4 个 split；当块为 128MB 时，会被划分为 8 个 split。</p></li><li><p>文件的大小。当块为 128MB 时，如果输入文件为 128MB，会被划分为 1 个 split；当块为 256MB，会被划分为 2 个 split。</p></li><li><p>文件的个数。FileInputFormat 按照文件分割 split，并且只会分割大文件，即那些大小超过 HDFS 块的大小的文件。如果 HDFS 中 <code>dfs.block.size</code> 设置为 64MB，而输入的目录中文件有 100 个，则划分后的 split 个数至少为 100 个。</p></li><li><p>splitSize 的大小。分片是按照 splitSize 的大小进行分割的，一个 split 的大小在没有设置的情况下，默认等于 HDFS Block 的大小。但应用程序可以通过两个参数来对 splitSize 进行调节。</p><ul><li>maxSize：split 的最大值，默认是 <code>Long.MAX_VALUE</code>。可以通过<code>mapreduce.input.fileinputformat.split.maxsize</code> 进行设置。</li><li>minSize：split 的最小值，该值可由两个途径设置：(1)通过子类重写方法 <code>protected void setMinSplitSize(long minSplitSize)</code> 进行设置。一般情况为 <code>1</code>，特殊情况除外；(2)通过配置文件中的 <code>mapreduce.input.fileinputformat.split.minsize</code> 进行设置。</li></ul></li></ul></li><li><p><strong>reduce 数量</strong></p><p>  reduce 的数量 <code>job.setNumReduceTasks(x);</code> x 为 reduce 的数量。不设置的话默认为 1。</p></li></ul><h3 id="在一个运行的-Hadoop-任务中，什么是-InputSplit？"><a href="#在一个运行的-Hadoop-任务中，什么是-InputSplit？" class="headerlink" title="在一个运行的 Hadoop 任务中，什么是 InputSplit？"></a>在一个运行的 Hadoop 任务中，什么是 InputSplit？</h3><p>FileInputFormat 源码解析 <code>input.getSplits(job)</code></p><ol><li>找到你数据存储的目录；</li><li>开始遍历处理（规划切片）目录下的每一个文件；</li><li>遍历第一个文件 ss.txt；<ol><li>获取文件大小 <code>fs.sizeOf(ss.txt)</code>;</li><li>计算切片大小 <code>computeSliteSize(Math.max(minSize, Math.min(maxSize, blocksize))) = blocksize = 128MB</code>；</li><li>默认情况下，切片大小 = blocksize；</li><li>开始切，形成第 1 个切片：ss.txt—0:128MB，第 2 个切片 ss.txt—128:256MB，第 3 个切片 ss.txt—256MB:300MB（每次切片时，都要判断切完剩下的部分是否大于块的 1.1 倍，不大于 1.1 倍就划分为一块切片）；</li><li>将切片信息写到一个切片规划文件中；</li><li>整个切片的核心过程在 <code>getSplit()</code> 方法中完成；</li><li>数据切片只是在逻辑上对输入数据进行分片，并不会再磁盘上将其切分成分片进行存储。InputSplit 只记录了分片的元数据信息，比如起始位置、长度以及所在的节点列表等；</li><li>注意：block 是 HDFS 上物理上存储的存储的数据，切片是对数据逻辑上的划分。</li></ol></li><li>提交切片规划文件到 YARN 上，YARN 上的 MRAppMaster 就可以根据切片规划文件计算开启 MapTask 个数，一个 job 的 map 阶段 MapTask 并行度（个数），也是由客户端提交 job 时的切片（split）个数决定。</li></ol><h3 id="描述-MapReduce-有几种排序及排序发生的阶段？"><a href="#描述-MapReduce-有几种排序及排序发生的阶段？" class="headerlink" title="描述 MapReduce 有几种排序及排序发生的阶段？"></a>描述 MapReduce 有几种排序及排序发生的阶段？</h3><p><strong>排序的分类：</strong></p><ul><li><p>部分排序：MapReduce 根据输入记录的键对数据集排序。保证输出的每个文件内部排序；</p></li><li><p>全排序：如何用 Hadoop 产生一个全局排序的文件？最简单的方法是使用一个分区。但该方法在处理大型文件时效率极低，因为一台机器必须处理所有输出文件，从而完全丧失了 MapReduce 所提供的并行架构；</p><p>替代方案：首先创建一系列排好序的文件；其次，串联这些文件；最后，生成一个全局排序的文件。主要思路是使用一个分区来描述输出的全局排序。例如：可以为待分析文件创建 3 个分区，在第一分区中，记录的单词首字母 a-g，第二分区记录单词首字母 h-n, 第三分区记录单词首字母 o-z；</p></li><li><p>辅助排序：（GroupingComparator 分组）：MapReduce 框架在记录到达 reducer 之前按键对记录排序，但键所对应的值并没有被排序。甚至在不同的执行轮次中，这些值的排序也不固定，因为它们来自不同的 map 任务且这些 map 任务在不同轮次中完成时间各不相同。一般来说，大多数 MapReduce 程序会避免让 reduce 函数依赖于值的排序。但是，有时也需要通过特定的方法对键进行排序和分组等以实现对值的排序；</p></li><li><p>二次排序：在自定义排序过程中，如果 <code>compareTo</code> 中的判断条件为两个即为二次排序。</p></li></ul><p><strong>自定义排序 WritableComparable：</strong></p><p>bean 对象实现 WritableComparable 接口重写 <code>compareTo</code> 方法，就可以实现排序。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">compareTo</span><span class="params">(FlowBean o)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 倒序排列，从大到小</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.sumFlow &gt; o.getSumFlow() ? -<span class="number">1</span> : <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>排序发生的阶段：</strong></p><ul><li>一个是在 map side 发生在 spill 后 partition 前；</li><li>一个是在 reduce side 发生在 copy 后 reduce 前。</li></ul><h3 id="MapReduce-的-shuffle-过程？"><a href="#MapReduce-的-shuffle-过程？" class="headerlink" title="MapReduce 的 shuffle 过程？"></a>MapReduce 的 shuffle 过程？</h3><p>Shuffle 机制：MapReduce 保证每个 reducer 的输入都是按键有序排列的，系统执行排序的过程（即将 map 输出作为输入传给 reducer）称为 shuffle。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/mapreduce_shuffle.png" alt="MapReduce Shuffle"></p><p>分区，排序，溢写，拷贝到对应 reduce 机器上，增加 combiner，压缩溢写的文件。</p><h3 id="如何优化-shuffle？"><a href="#如何优化-shuffle？" class="headerlink" title="如何优化 shuffle？"></a>如何优化 shuffle？</h3><p><strong>Map 端 shuffle</strong></p><ul><li>kvbuffer，默认是 100MB，可以通过参数 <code>mapreduce.task.io.sort.mb</code> 来修改，一般不修改；</li><li>缓冲区阈值，一般是 80%，可以通过 <code>mapreduce.map.sort.spill.percent</code> 来修改；</li><li>合并 spill 文件，<code>mapreduce.task.io.sort.factor</code> 属性配置每次最多合并多少个文件，默认为 10，即一次最多合并 10 个 spill 文件.如果 spill 文件数量大于 <code>mapreduce.map.combiner.minspills</code> 配置的数，则在合并文件写入之前，会再次运行 combiner。如果 spill 文件数量太少，运行 combiner 的收益可能小于调用的代价；</li><li>对 map 输出进行压缩，在数据量大的时候，可以对 map 输出进行压缩，要启用压缩，将 <code>mapreduce.map.output.compress</code> 设为 true，并使用 <code>mapreduce.map.output.compress.codec</code> 设置使用的压缩算法。</li></ul><p><strong>Reduce 端 shuffle</strong></p><ul><li>copy 线程数量：copy 是用来从 map 任务中提取数据的，默认为 5 个 copy 线程，可以通是 <code>mapreduce.reduce.shuffle.parallelcopies</code> 配置。</li><li>内存分配：如果能够让所有数据都保存在内存中，可以达到最佳的性能。通常情况下，内存都保留给 <code>reduce</code> 函数，但是如果 <code>reduce</code> 函数对内存需求不是很高，将 <code>mapreduce.reduce.merge.inmem.threshold</code>（触发合并的 map 输出文件数）设为 0，<code>mapreduce.reduce.input.buffer.percent</code>（用于保存 map 输出文件的堆内存比例）设为1.0。</li></ul><h3 id="Partion-分区"><a href="#Partion-分区" class="headerlink" title="Partion 分区"></a>Partion 分区</h3><p>Partion 作用主要是对 map 处理的数据进行分区，可以解决数据倾斜的问题。</p><ul><li>分区是根据 MR 的输出 <code>&lt;key，value&gt;</code> 进行分区的。默认情况下，MR 的输出只有一个分区，一个分区就是一个文件。</li><li>自定义分区：继承 Partitioner，重写 <code>getPartition</code> 这个方法。</li><li>如果没有定义 Partitioner，则使用默认的 partition 算法，即根据每一条数据的 key 的 hashcode 值摸运算（%）reduce 的数量，得到的数字就是“分区号”。</li></ul><h3 id="描述-MapReduce-中-Combiner-的作用是什么，一般使用情景，哪些情况不需要，以及和-Reduce-的区别？"><a href="#描述-MapReduce-中-Combiner-的作用是什么，一般使用情景，哪些情况不需要，以及和-Reduce-的区别？" class="headerlink" title="描述 MapReduce 中 Combiner 的作用是什么，一般使用情景，哪些情况不需要，以及和 Reduce 的区别？"></a>描述 MapReduce 中 Combiner 的作用是什么，一般使用情景，哪些情况不需要，以及和 Reduce 的区别？</h3><ul><li>Combiner 是一种特殊的 Reducer，它的意义就是对每一个 MapTask 的输出进行<strong>局部汇总</strong>，以减小网络传输量；</li><li>Combiner 在 Mapper 端执行一次合并，用于减少 Mapper 输出到 Reducer 的数量，可以调高效率；</li><li>Combiner 能够应用的前提是不能影响最终的业务逻辑，而且，Combiner 的输出 kv 应该跟 Reducer 的输入 kv 类型要对应起来；</li><li>Combiner 和 Reducer 的区别在于运行的位置；<ul><li>Combiner 是在每一个 MapTask 所在的节点运行；</li><li>Reducer 是接收全局所有 Mapper 的输出结果。</li></ul></li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/mapreduce_combiner.png" alt="MapReduce Combiner"></p><h2 id="Hadoop-YARN"><a href="#Hadoop-YARN" class="headerlink" title="Hadoop YARN"></a>Hadoop YARN</h2><h3 id="简述-hadoop1-x-与-hadoop2-x-的架构异同"><a href="#简述-hadoop1-x-与-hadoop2-x-的架构异同" class="headerlink" title="简述 hadoop1.x 与 hadoop2.x 的架构异同"></a>简述 hadoop1.x 与 hadoop2.x 的架构异同</h3><ul><li>加入了 yarn 解决了资源调度的问题;</li><li>加入了对 zookeeper 的支持实现比较可靠的高可用。</li></ul><h3 id="为什么会产生-yarn，它解决了什么问题，有什么优势？"><a href="#为什么会产生-yarn，它解决了什么问题，有什么优势？" class="headerlink" title="为什么会产生 yarn，它解决了什么问题，有什么优势？"></a>为什么会产生 yarn，它解决了什么问题，有什么优势？</h3><ul><li>Yarn 最主要的功能就是解决运行的用户程序与 yarn 框架完全解耦。</li><li>Yarn 上可以运行各种类型的分布式运算程序（mapreduce 只是其中的一种），比如 mapreduce、storm 程序，spark 程序等等。</li></ul><h3 id="yarn-包括什么组件，各自的作用是什么？"><a href="#yarn-包括什么组件，各自的作用是什么？" class="headerlink" title="yarn 包括什么组件，各自的作用是什么？"></a>yarn 包括什么组件，各自的作用是什么？</h3><p>yarn 是一个资源管理、任务调度的框架。主要包含三个模块：ResourceManger、NodeManger、ApplicationMater。</p><ul><li>ResourceManger：负责所有资源的监控、分配和管理；</li><li>ApplicationMater：负责每一个具体应用程序的调度和协调，用户提交的每个应用程序均包含一个 ApplicationMater，它可以运行在 ResourceManger 以外的机器上；</li><li>NodeManger：负责每一个节点的维护。</li></ul><h3 id="Hadoop-有哪些调度器？"><a href="#Hadoop-有哪些调度器？" class="headerlink" title="Hadoop 有哪些调度器？"></a>Hadoop 有哪些调度器？</h3><ul><li>默认的调度器 FIFO：Hadoop 中默认的调度器，它先按照作业的优先级高低，再按照到达时间的先后选择被执行的作业。</li><li>计算能力调度器 Capacity Scheduler：支持多个队列，每个队列可配置一定的资源量，每个队列采用 FIFO 调度策略，为了防止同一个用户的作业独占队列中的资源，该调度器会对同一用户提交的作业所占资源量进行限定。调度时，首先按以下策略选择一个合适队列：计算每个队列中正在运行的任务数与其应该分得的计算资源之间的比值，选择一个该比值最小的队列；然后按以下策略选择该队列中一个作业：按照作业优先级和提交时间顺序选择，同时考虑用户资源量限制和内存限制。</li><li>公平调度器 Fair Scheduler：同计算能力调度器类似，支持多队列多用户，每个队列中的资源量可以配置，同一队列中的作业公平共享队列中所有资源。实际上，Hadoop 的调度器远不止以上三种，最近，出现了很多针对新型应用的 Hadoop 调度器。</li></ul><h3 id="什么是-Container？"><a href="#什么是-Container？" class="headerlink" title="什么是 Container？"></a>什么是 Container？</h3><p>Container 是一个抽象概念，称之为容器，包含任务运行时所需的资源（包括内存、硬盘、cpu 等）和环境（包含启动命令、环境变量等）。</p><h3 id="yarn-的执行流程？"><a href="#yarn-的执行流程？" class="headerlink" title="yarn 的执行流程？"></a>yarn 的执行流程？</h3><ol><li>客户端向集群提交一个任务，该任务首先到 RM 中的 AM；</li><li>AM 收到任务后，会在集群中找一个 NodeManger，在该 NodeManger 上启动一个 APPMaster 进程。该进程用于执行任务划分和任务监控；</li><li>AppMaster 启动起来之后，会向 RM 中的 AM 注册信息，APPMaster 向 RM 下的 ResourceSchedule 申请计算任务所需的资源；</li><li>AppMaster 申请到资源之后，会与所有 NodeManger 通信要求他们启动所有计算任务（Map 和 Reduce）；</li><li>各个 NM 启动对应的容器 Container 用来执行 Map 和 Reduce 任务；</li><li>各个任务会向 APPMaster 汇报自己的执行进度和执行状况，以便让 AppMaster 随时掌握各个任务的运行状态，在某个任务出了问题之后重启执行该任务；</li><li>在执行完之后，APPMaster 会向 AM 汇报，以便让 ApplicationManger 注销并关闭自己，使得资源得以回收。</li></ol><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/bigdata_interview/yarn.png" alt="Yarn 执行流程"></p><h3 id="MapReduce-容错性？"><a href="#MapReduce-容错性？" class="headerlink" title="MapReduce 容错性？"></a>MapReduce 容错性？</h3><p>1）MRAppMaster 容错性  一旦运行失败，由 YARN 的 ResourceManager 负责重新启动，最多重启次数可由用户设置，默认是 2 次。一旦超过最高重启次数，则作业运行失败。2）Map/Reduce Task  Task Task 周期性向 MRAppMaster 汇报心跳；一旦 Task 挂掉，则 MRAppMaster 将为之重新申请资源，并运行之。最多重新运行次数可由用户设置，默认 4 次。</p><h3 id="MapReduce-2-x-推测执行算法及原理？"><a href="#MapReduce-2-x-推测执行算法及原理？" class="headerlink" title="MapReduce 2.x 推测执行算法及原理？"></a>MapReduce 2.x 推测执行算法及原理？</h3><ul><li>作业完成时间取决于最慢的任务完成时间<ul><li>一个作业由若干个 Map 任务和 Reduce 任务构成。因硬件老化、软件 Bug 等，某些任务可能运行非常慢。</li><li>典型案例：系统中有 99% 的 Map 任务都完成了，只有少数几个 Map 老是进度很慢，完不成，怎么办？</li></ul></li><li>推测执行机制<ul><li>发现拖后腿的任务，比如某个任务运行速度远慢于任务平均速度。为拖后腿任务启动一个备份任务，同时运行。谁先运行完，则采用谁的结果。</li></ul></li><li>不能启用推测执行机制情况<ul><li>任务间存在严重的负载倾斜；</li><li>特殊任务，比如任务向数据库中写数据。</li></ul></li><li><p>算法原理</p><p>假设某一时刻，任务 <code>T</code> 的执行进度为 <code>progress</code>，则可通过一定的算法推测出该任务的最终完成时刻 <code>estimateEndTime</code>。另一方面，如果此刻为该任务启动一个备份任务，则可推断出它可能的完成时刻 <code>estimateEndTime</code>,于是可得出以下几个公式：</p><ul><li><code>estimateEndTime = estimatedRunTime + taskStartTime</code></li><li><code>estimatedRunTime = (currentTimestamp - taskStartTime)/progress</code></li><li><code>estimateEndTime = currentTimestamp + averageRunTime</code></li></ul><p>其中，<code>currentTimestamp</code> 为当前时刻；<code>taskStartTime</code> 为该任务的启动时刻；<code>averageRunTime</code> 为已经成功运行完成的任务的平均运行时间。这样，MRv2 总是选择 <code>estimateEndTime- estimateEndTime</code> 差值最大的任务，并为之启动备份任务。为了防止大量任务同时启动备份任务造成的资源浪费，MRv2 为每个作业设置了同时启动的备份任务数目上限。</p><p>推测执行机制实际上采用了经典的算法优化方法：以空间换时间，它同时启动多个相同任务处理相同的数据，并让这些任务竞争以缩短数据处理时间。显然，这种方法需要占用更多的计算资源。在集群资源紧缺的情况下，应合理使用该机制，争取在多用少量资源的情况下，减少作业的计算时间。</p></li></ul><h2 id="Hadoop-调优"><a href="#Hadoop-调优" class="headerlink" title="Hadoop 调优"></a>Hadoop 调优</h2><h3 id="MapReduce-跑得慢的原因？"><a href="#MapReduce-跑得慢的原因？" class="headerlink" title="MapReduce 跑得慢的原因？"></a>MapReduce 跑得慢的原因？</h3><p>MapReduce 程序效率的瓶颈在于两点：</p><ul><li>计算机性能<ul><li>CPU、内存、磁盘健康、网络</li></ul></li><li>I/O 操作优化<ul><li>数据倾斜</li><li>map 和 reduce 数设置不合理</li><li>reduce 等待过久</li><li>小文件过多</li><li>大量的不可分块的超大文件</li><li>spill 次数过多</li><li>merge 次数过多等</li></ul></li></ul><h3 id="MapReduce-优化方法？"><a href="#MapReduce-优化方法？" class="headerlink" title="MapReduce 优化方法？"></a>MapReduce 优化方法？</h3><ol><li><p>数据输入</p><ul><li>合并小文件：在执行 mr 任务前将小文件进行合并，大量的小文件会产生大量的 map 任务，增大 map 任务装载次数，而任务的装载比较耗时，从而导致 mr 运行较慢。</li><li>采用 CombineFileInputFormat 来作为输入，解决输入端大量小文件场景。</li></ul></li><li><p>map 阶段</p><ul><li>减少 spill 次数：通过调整 <code>io.sort.mb</code> 及 <code>sort.spill.percent</code>参数值，增大触发 spill 的内存上限，减少 spill 次数，从而减少磁盘 IO。</li><li>减少 merge 次数：通过调整 <code>io.sort.factor</code> 参数，增大 merge 的文件数目，减少 merge 的次数，从而缩短 mr 处理时间。</li><li>在 map 之后先进行 combine 处理，减少 I/O。</li></ul></li><li><p>reduce 阶段</p><ul><li>合理设置 map 和 reduce 数：两个都不能设置太少，也不能设置太多。太少，会导致 task 等待，延长处理时间；太多，会导致 map、reduce 任务间竞争资源，造成处理超时等错误。</li><li>设置 map、reduce 共存：调整 <code>slowstart.completedmaps</code>参数，使 map 运行到一定程度后，reduce 也开始运行，减少 reduce 的等待时间。</li><li>规避使用 reduce，因为 reduce 在用于连接数据集的时候将会产生大量的网络消耗。</li><li>合理设置 reduce 端的 buffer，默认情况下，数据达到一个阈值的时候，buffer 中的数据就会写入磁盘，然后 reduce 会从磁盘中获得所有的数据。也就是说，buffer 和 reduce 是没有直接关联的，中间多个一个写磁盘 -&gt; 读磁盘的过程，既然有这个弊端，那么就可以通过参数来配置，使得 buffer 中的一部分数据可以直接输送到 reduce，从而减少 IO 开销：<code>mapred.job.reduce.input.buffer.percent</code>，默认为 0.0。当值大于 0 的时候，会保留指定比例的内存读 buffer 中的数据直接拿给 reduce 使用。这样一来，设置 buffer 需要内存，读取数据需要内存，reduce 计算也要内存，所以要根据作业的运行情况进行调整。</li></ul></li><li><p>IO 传输</p><ul><li>采用数据压缩的方式，减少网络 IO 的时间。安装 Snappy 和 LZOP 压缩编码器。</li><li>使用 SequenceFile 二进制文件</li></ul></li><li><p>数据倾斜问题</p><ol><li>数据倾斜现象：<ul><li>数据频率倾斜：某一个区域的数据量要远远大于其他区域。</li><li>数据大小倾斜：部分记录的大小远远大于平均值。</li></ul></li><li><p>如何收集倾斜数据</p><ul><li>在 reduce 方法中加入记录 map 输出键的详细情况的功能。</li></ul><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String MAX_VALUES = <span class="string">&quot;skew.maxvalues&quot;</span>;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> maxValueThreshold;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">configure</span><span class="params">(JobConf job)</span> </span>&#123;</span><br><span class="line">    maxValueThreshold = job.getInt(MAX_VALUES, <span class="number">100</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">reduce</span><span class="params">(Text key, Iterator&lt;Text&gt; values,</span></span></span><br><span class="line"><span class="function"><span class="params">                    OutputCollector&lt;Text, Text&gt; output,</span></span></span><br><span class="line"><span class="function"><span class="params">                    Reporter reporter)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (values.hasNext()) &#123;</span><br><span class="line">        values.next();</span><br><span class="line">        i++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (++i &gt; maxValueThreshold) &#123;</span><br><span class="line">        log.info(<span class="string">&quot;Received &quot;</span> + i + <span class="string">&quot; values for key &quot;</span> + key);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li><li><p>减少数据倾斜的方法</p><ul><li>抽样和范围分区：可以通过对原始数据进行抽样得到的结果集来预设分区边界值。</li><li>自定义分区：另一个抽样和范围分区的替代方案是基于输出键的背景知识进行自定义分区。例如，如果 map 输出键的单词来源于一本书。其中大部分必然是省略词（stopword）。那么就可以将自定义分区将这部分省略词发送给固定的一部分 reduce 实例。而将其他的都发送给剩余的 reduce 实例。</li><li>Combine：使用 Combine 可以大量地减小数据频率倾斜和数据大小倾斜。在可能的情况下，Combine 的目的就是聚合并精简数据。</li></ul></li></ol></li></ol><h3 id="HDFS-小文件优化方法？"><a href="#HDFS-小文件优化方法？" class="headerlink" title="HDFS 小文件优化方法？"></a>HDFS 小文件优化方法？</h3><ol><li><p>HDFS 小文件弊端：</p><p> HDFS 上每个文件都要在 NameNode 上建立一个索引，这个索引的大小约为 150byte，这样当小文件比较多的时候，就会产生很多的索引文件，一方面会大量占用 NameNode 的内存空间，另一方面就是索引文件过大是的索引速度变慢。</p></li><li><p>解决的方式：</p><ul><li>Hadoop 本身提供了一些文件压缩的方案。</li><li>从系统层面改变现有 HDFS 存在的问题，其实主要还是小文件的合并，然后建立比较快速的索引。</li></ul></li><li><p>Hadoop 自带小文件解决方案</p><ul><li>Hadoop Archive：是一个高效地将小文件放入 HDFS 块中的文件存档工具，它能够将多个小文件打包成一个 HAR 文件，这样在减少 NameNode 内存使用的同时。</li><li>Sequence file：Sequence file 由一系列的二进制 key/value 组成，如果为 key 小文件名，value 为文件内容，则可以将大批小文件合并成一个大文件。</li><li>CombineFileInputFormat：CombineFileInputFormat 是一种新的 inputFormat，用于将多个文件合并成一个单独的 split，另外，它会考虑数据的存储位置。</li></ul></li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Hadoop/">Hadoop</category>
      
      
      <comments>https://blog.eurkon.com/post/2b822834.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>大数据面试题解析</title>
      <link>https://blog.eurkon.com/post/7e24cf66.html</link>
      <guid>https://blog.eurkon.com/post/7e24cf66.html</guid>
      <pubDate>Thu, 25 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Hadoop-面试题解析&quot;&gt;&lt;a href=&quot;#Hadoop-面试题解析&quot; class=&quot;headerlink&quot; title=&quot;Hadoop 面试题解析&quot;&gt;&lt;/a&gt;&lt;a href=&quot;/post/2b822834.html&quot;&gt;Hadoop 面试题解析&lt;/a&gt;&lt;/h2</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Hadoop-面试题解析"><a href="#Hadoop-面试题解析" class="headerlink" title="Hadoop 面试题解析"></a><a href="/post/2b822834.html">Hadoop 面试题解析</a></h2><h2 id="Zookeeper-面试题解析"><a href="#Zookeeper-面试题解析" class="headerlink" title="Zookeeper 面试题解析"></a><a href="/post/2f1ea7f2.html">Zookeeper 面试题解析</a></h2><h2 id="Flume-面试题解析"><a href="#Flume-面试题解析" class="headerlink" title="Flume 面试题解析"></a><a href="/post/5a2a12a6.html">Flume 面试题解析</a></h2><h2 id="Kafka-面试题解析"><a href="#Kafka-面试题解析" class="headerlink" title="Kafka 面试题解析"></a><a href="/post/89cabcb1.html">Kafka 面试题解析</a></h2><h2 id="Hive-面试题解析"><a href="#Hive-面试题解析" class="headerlink" title="Hive 面试题解析"></a><a href="/post/62c9bbde.html">Hive 面试题解析</a></h2><h2 id="HBase-面试题解析"><a href="#HBase-面试题解析" class="headerlink" title="HBase 面试题解析"></a><a href="/post/47457d03.html">HBase 面试题解析</a></h2><h2 id="Sqoop-面试题解析"><a href="#Sqoop-面试题解析" class="headerlink" title="Sqoop 面试题解析"></a><a href="/post/420614eb.html">Sqoop 面试题解析</a></h2><h2 id="MySQL-面试题解析"><a href="#MySQL-面试题解析" class="headerlink" title="MySQL 面试题解析"></a><a href="/post/54dbb40b.html">MySQL 面试题解析</a></h2><h2 id="Spark-面试题解析"><a href="#Spark-面试题解析" class="headerlink" title="Spark 面试题解析"></a><a href="/post/cccf27af.html">Spark 面试题解析</a></h2><h2 id="Elasticsearch-面试题解析"><a href="#Elasticsearch-面试题解析" class="headerlink" title="Elasticsearch 面试题解析"></a><a href="/post/fae3ec89.html">Elasticsearch 面试题解析</a></h2>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Hadoop/">Hadoop</category>
      
      <category domain="https://blog.eurkon.com/tags/Flume/">Flume</category>
      
      <category domain="https://blog.eurkon.com/tags/Sqoop/">Sqoop</category>
      
      <category domain="https://blog.eurkon.com/tags/Zookeeper/">Zookeeper</category>
      
      <category domain="https://blog.eurkon.com/tags/Kafka/">Kafka</category>
      
      <category domain="https://blog.eurkon.com/tags/Hive/">Hive</category>
      
      <category domain="https://blog.eurkon.com/tags/HBase/">HBase</category>
      
      <category domain="https://blog.eurkon.com/tags/MySQL/">MySQL</category>
      
      <category domain="https://blog.eurkon.com/tags/Spark/">Spark</category>
      
      
      <comments>https://blog.eurkon.com/post/7e24cf66.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Java 面试题解析（Java EE）</title>
      <link>https://blog.eurkon.com/post/dc04fa32.html</link>
      <guid>https://blog.eurkon.com/post/dc04fa32.html</guid>
      <pubDate>Tue, 23 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;什么是-ORM？&quot;&gt;&lt;a href=&quot;#什么是-ORM？&quot; class=&quot;headerlink&quot; title=&quot;什么是 ORM？&quot;&gt;&lt;/a&gt;什么是 ORM？&lt;/h2&gt;&lt;p&gt;答：对象关系映射（Object-Relational Mapping，简称 ORM）是一种为</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="什么是-ORM？"><a href="#什么是-ORM？" class="headerlink" title="什么是 ORM？"></a>什么是 ORM？</h2><p>答：对象关系映射（Object-Relational Mapping，简称 ORM）是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术；简单的说，ORM 是通过使用描述对象和数据库之间映射的元数据（在 Java 中可以用 XML 或者是注解），将程序中的对象自动持久化到关系数据库中或者将关系数据库表中的行转换成 Java 对象，其本质上就是将数据从一种形式转换到另外一种形式。</p><h2 id="持久层设计要考虑的问题有哪些？你用过的持久层框架有哪些？"><a href="#持久层设计要考虑的问题有哪些？你用过的持久层框架有哪些？" class="headerlink" title="持久层设计要考虑的问题有哪些？你用过的持久层框架有哪些？"></a>持久层设计要考虑的问题有哪些？你用过的持久层框架有哪些？</h2><p>答：所谓“持久”就是将数据保存到可掉电式存储设备中以便今后使用，简单的说，就是将内存中的数据保存到关系型数据库、文件系统、消息队列等提供持久化支持的设备中。持久层就是系统中专注于实现数据持久化的相对独立的层面。</p><p>持久层设计的目标包括：</p><ul><li>数据存储逻辑的分离，提供抽象化的数据访问接口。</li><li>数据访问底层实现的分离，可以在不修改代码的情况下切换底层实现。</li><li>资源管理和调度的分离，在数据访问层实现统一的资源调度（如缓存机制）。</li><li>数据抽象，提供更面向对象的数据操作。</li></ul><p>持久层框架有：</p><ul><li>Hibernate</li><li>MyBatis</li><li>TopLink</li><li>Guzz</li><li>jOOQ</li><li>Spring Data</li><li>ActiveJDBC</li></ul><h2 id="Hibernate-中-SessionFactory-是线程安全的吗？Session-是线程安全的吗（两个线程能够共享同一个-Session-吗）？"><a href="#Hibernate-中-SessionFactory-是线程安全的吗？Session-是线程安全的吗（两个线程能够共享同一个-Session-吗）？" class="headerlink" title="Hibernate 中 SessionFactory 是线程安全的吗？Session 是线程安全的吗（两个线程能够共享同一个 Session 吗）？"></a>Hibernate 中 SessionFactory 是线程安全的吗？Session 是线程安全的吗（两个线程能够共享同一个 Session 吗）？</h2><p>答：SessionFactory 对应 Hibernate 的一个数据存储的概念，它是线程安全的，可以被多个线程并发访问。SessionFactory 一般只会在启动的时候构建。对于应用程序，最好将 SessionFactory 通过单例模式进行封装以便于访问。Session 是一个轻量级非线程安全的对象（线程间不能共享 Session），它表示与数据库进行交互的一个工作单元。Session 是由 SessionFactory 创建的，在任务完成之后它会被关闭。Session 是持久层服务对外提供的主要接口。Session 会延迟获取数据库连接（也就是在需要的时候才会获取）。为了避免创建太多的 Session，可以使用 <code>ThreadLocal</code> 将 Session 和当前线程绑定在一起，这样可以让同一个线程获得的总是同一个 Session。Hibernate 3 中 SessionFactory 的 <code>getCurrentSession()</code> 方法就可以做到。</p><h2 id="Hibernate-中-Session-的-load-和-get-方法的区别是什么？"><a href="#Hibernate-中-Session-的-load-和-get-方法的区别是什么？" class="headerlink" title="Hibernate 中 Session 的 load 和 get 方法的区别是什么？"></a>Hibernate 中 Session 的 <code>load</code> 和 <code>get</code> 方法的区别是什么？</h2><p>答：主要有以下三项区别：</p><ol><li>如果没有找到符合条件的记录，<code>get</code> 方法返回 <code>null</code>，<code>load</code> 方法抛出异常。</li><li><code>get</code> 方法直接返回实体类对象，<code>load</code> 方法返回实体类对象的代理。</li><li>在 Hibernate 3 之前，<code>get</code> 方法只在一级缓存中进行数据查找，如果没有找到对应的数据则越过二级缓存，直接发出 SQL 语句完成数据读取；<code>load</code> 方法则可以从二级缓存中获取数据；从 Hibernate 3 开始，<code>get</code> 方法不再是对二级缓存只写不读，它也是可以访问二级缓存的。</li></ol><p><strong>说明：</strong>对于 <code>load()</code> 方法 Hibernate 认为该数据在数据库中一定存在可以放心的使用代理来实现延迟加载，如果没有数据就抛出异常，而通过 <code>get()</code> 方法获取的数据可以不存在。</p><h2 id="Session-的-save-、update-、merge-、lock-、saveOrUpdate-和-persist-方法分别是做什么的？有什么区别？"><a href="#Session-的-save-、update-、merge-、lock-、saveOrUpdate-和-persist-方法分别是做什么的？有什么区别？" class="headerlink" title="Session 的 save()、update()、merge()、lock()、saveOrUpdate()和 persist() 方法分别是做什么的？有什么区别？"></a>Session 的 <code>save()</code>、<code>update()</code>、<code>merge()</code>、<code>lock()</code>、<code>saveOrUpdate()</code>和 <code>persist()</code> 方法分别是做什么的？有什么区别？</h2><p>答：Hibernate 的对象有三种状态：瞬时态（transient）、持久态（persistent）和游离态（detached），如第 135 题中的图所示。瞬时态的实例可以通过调用 <code>save()</code>、<code>persist()</code> 或者 <code>saveOrUpdate()</code> 方法变成持久态；游离态的实例可以通过调用 <code>update()</code>、<code>saveOrUpdate()</code>、<code>lock()</code> 或者 <code>replicate()</code> 变成持久态。<code>save()</code> 和 <code>persist()</code> 将会引发 SQL 的 <code>INSERT</code> 语句，而 <code>update()</code> 或 <code>merge()</code> 会引发 <code>UPDATE</code> 语句。<code>save()</code> 和 <code>update()</code> 的区别在于一个是将瞬时态对象变成持久态，一个是将游离态对象变为持久态。<code>merge()</code> 方法可以完成 <code>save()</code> 和 <code>update()</code> 方法的功能，它的意图是将新的状态合并到已有的持久化对象上或创建新的持久化对象。对于 <code>persist()</code> 方法，按照官方文档的说明：(1)<code>persist()</code> 方法把一个瞬时态的实例持久化，但是并不保证标识符被立刻填入到持久化实例中，标识符的填入可能被推迟到 <code>flush</code> 的时间；(2)<code>persist()</code> 方法保证当它在一个事务外部被调用的时候并不触发一个 <code>INSERT</code> 语句，当需要封装一个长会话流程的时候，<code>persist()</code> 方法是很有必要的；(3)<code>save()</code> 方法不保证第(2)条，它要返回标识符，所以它会立即执行 <code>INSERT</code> 语句，不管是在事务内部还是外部。至于 <code>lock()</code> 方法和 <code>update()</code> 方法的区别，<code>update()</code> 方法是把一个已经更改过的脱管状态的对象变成持久状态；<code>lock()</code> 方法是把一个没有更改过的脱管状态的对象变成持久状态。</p><h2 id="阐述-Session-加载实体对象的过程。"><a href="#阐述-Session-加载实体对象的过程。" class="headerlink" title="阐述 Session 加载实体对象的过程。"></a>阐述 Session 加载实体对象的过程。</h2><p>答：Session 加载实体对象的步骤是：</p><ol><li>Session 在调用数据库查询功能之前，首先会在一级缓存中通过实体类型和主键进行查找，如果一级缓存查找命中且数据状态合法，则直接返回；</li><li>如果一级缓存没有命中，接下来 Session 会在当前 <code>NonExists</code> 记录（相当于一个查询黑名单，如果出现重复的无效查询可以迅速做出判断，从而提升性能）中进行查找，如果 <code>NonExists</code> 中存在同样的查询条件，则返回 <code>null</code>；</li><li>如果一级缓存查询失败则查询二级缓存，如果二级缓存命中则直接返回；</li><li>如果之前的查询都未命中，则发出 SQL 语句，如果查询未发现对应记录则将此次查询添加到 Session 的 <code>NonExists</code> 中加以记录，并返回 <code>null</code>；</li><li>根据映射配置和 SQL 语句得到 <code>ResultSet</code>，并创建对应的实体对象；</li><li>将对象纳入 Session（一级缓存）的管理；</li><li>如果有对应的拦截器，则执行拦截器的 <code>onLoad</code> 方法；</li><li>如果开启并设置了要使用二级缓存，则将数据对象纳入二级缓存；</li><li>返回数据对象。</li></ol><h2 id="Query-接口的-list-方法和-iterate-方法有什么区别？"><a href="#Query-接口的-list-方法和-iterate-方法有什么区别？" class="headerlink" title="Query 接口的 list 方法和 iterate 方法有什么区别？"></a><code>Query</code> 接口的 <code>list</code> 方法和 <code>iterate</code> 方法有什么区别？</h2><p>答：</p><ul><li><code>list()</code> 方法无法利用一级缓存和二级缓存（对缓存只写不读），它只能在开启查询缓存的前提下使用查询缓存；<code>iterate()</code> 方法可以充分利用缓存，如果目标数据只读或者读取频繁，使用 <code>iterate()</code> 方法可以减少性能开销。</li><li><code>list()</code> 方法不会引起 N+1 查询问题，而 <code>iterate()</code> 方法可能引起 N+1 查询问题</li></ul><p><strong>说明：</strong>关于 N+1 查询问题，可以参考 CSDN 的一篇文章<a href="http://blog.csdn.net/xtayhicbladwin/article/details/4739852">《什么是 N+1 查询》</a></p><h2 id="Hibernate-如何实现分页查询？"><a href="#Hibernate-如何实现分页查询？" class="headerlink" title="Hibernate 如何实现分页查询？"></a>Hibernate 如何实现分页查询？</h2><p>答：通过 Hibernate 实现分页查询，开发人员只需要提供 HQL 语句（调用 Session 的 <code>createQuery()</code> 方法）或查询条件（调用 Session 的 <code>createCriteria()</code> 方法）、设置查询起始行数（调用 <code>Query</code> 或 <code>Criteria</code> 接口的 <code>setFirstResult()</code> 方法）和最大查询行数（调用 <code>Query</code> 或 <code>Criteria</code> 接口的 <code>setMaxResults()</code> 方法），并调用 <code>Query</code> 或 <code>Criteria</code> 接口的 <code>list()</code> 方法，Hibernate 会自动生成分页查询的 SQL 语句。</p><h2 id="锁机制有什么用？简述-Hibernate-的悲观锁和乐观锁机制。"><a href="#锁机制有什么用？简述-Hibernate-的悲观锁和乐观锁机制。" class="headerlink" title="锁机制有什么用？简述 Hibernate 的悲观锁和乐观锁机制。"></a>锁机制有什么用？简述 Hibernate 的悲观锁和乐观锁机制。</h2><p>答：有些业务逻辑在执行过程中要求对数据进行排他性的访问，于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改，这就是所谓的锁机制。</p><p>Hibernate 支持悲观锁和乐观锁两种锁机制。</p><ul><li><p>悲观锁，顾名思义悲观的认为在数据处理过程中极有可能存在修改数据的并发事务（包括本系统的其他事务或来自外部系统的事务），于是将处理的数据设置为锁定状态。悲观锁必须依赖数据库本身的锁机制才能真正保证数据访问的排他性，关于数据库的锁机制和事务隔离级别在前面已经讨论过了。</p></li><li><p>乐观锁，顾名思义，对并发事务持乐观态度（认为对数据的并发操作不会经常性的发生），通过更加宽松的锁机制来解决由于悲观锁排他性的数据访问对系统性能造成的严重影响。最常见的乐观锁是通过数据版本标识来实现的，读取数据时获得数据的版本号，更新数据时将此版本号加 1，然后和数据库表对应记录的当前版本号进行比较，如果提交的数据版本号大于数据库中此记录的当前版本号则更新数据，否则认为是过期数据无法更新。Hibernate 中通过 Session 的 <code>get()</code> 和 <code>load()</code> 方法从数据库中加载对象时可以通过参数指定使用悲观锁；而乐观锁可以通过给实体类加整型的版本字段再通过 XML 或 <code>@Version</code> 注解进行配置。</p></li></ul><p><strong>注意：</strong>使用乐观锁会增加了一个版本字段，很明显这需要额外的空间来存储这个版本字段，浪费了空间，但是乐观锁会让系统具有更好的并发性，这是对时间的节省。因此乐观锁也是典型的空间换时间的策略。</p><h2 id="阐述实体对象的三种状态以及转换关系。"><a href="#阐述实体对象的三种状态以及转换关系。" class="headerlink" title="阐述实体对象的三种状态以及转换关系。"></a>阐述实体对象的三种状态以及转换关系。</h2><p>答：最新的 Hibernate 文档中为 Hibernate 对象定义了四种状态（原来是三种状态，面试的时候基本上问的也是三种状态），分别是：<strong>瞬时态</strong>（new, or transient）、<strong>持久态</strong>（managed, or persistent）、<strong>游状态</strong>（detached）和<strong>移除态</strong>（removed，以前 Hibernate 文档中定义的三种状态中没有移除态），如下图所示，就以前的 Hibernate 文档中移除态被视为是瞬时态。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/135.png" alt="实体对象的三种状态以及转换关系"></p><ul><li><p>瞬时态：当 new 一个实体对象后，这个对象处于瞬时态，即这个对象只是一个保存临时数据的内存区域，如果没有变量引用这个对象，则会被 JVM 的垃圾回收机制回收。这个对象所保存的数据与数据库没有任何关系，除非通过 Session 的 <code>save()</code>、<code>saveOrUpdate()</code>、<code>persist()</code>、<code>merge()</code> 方法把瞬时态对象与数据库关联，并把数据插入或者更新到数据库，这个对象才转换为持久态对象。</p></li><li><p>持久态：持久态对象的实例在数据库中有对应的记录，并拥有一个持久化标识（ID）。对持久态对象进行 <code>delete</code> 操作后，数据库中对应的记录将被删除，那么持久态对象与数据库记录不再存在对应关系，持久态对象变成移除态（可以视为瞬时态）。持久态对象被修改变更后，不会马上同步到数据库，直到数据库事务提交。</p></li><li><p>游离态：当 Session 进行了 <code>close()</code>、<code>clear()</code>、<code>evict()</code> 或 <code>flush()</code> 后，实体对象从持久态变成游离态，对象虽然拥有持久和与数据库对应记录一致的标识值，但是因为对象已经从会话中清除掉，对象不在持久化管理之内，所以处于游离态（也叫脱管态）。游离态的对象与临时状态对象是十分相似的，只是它还含有持久化标识。</p></li></ul><p><strong>注意：</strong>关于这个问题，在 <a href="https://docs.jboss.org/hibernate/orm/4.3/devguide/en-US/html_single/">Hibernate 的官方文档</a>中有更为详细的解读。</p><h2 id="如何理解-Hibernate-的延迟加载机制？在实际应用中，延迟加载与-Session-关闭的矛盾是如何处理的？"><a href="#如何理解-Hibernate-的延迟加载机制？在实际应用中，延迟加载与-Session-关闭的矛盾是如何处理的？" class="headerlink" title="如何理解 Hibernate 的延迟加载机制？在实际应用中，延迟加载与 Session 关闭的矛盾是如何处理的？"></a>如何理解 Hibernate 的延迟加载机制？在实际应用中，延迟加载与 Session 关闭的矛盾是如何处理的？</h2><p>答：延迟加载就是并不是在读取的时候就把数据加载进来，而是等到使用时再加载。Hibernate 使用了虚拟代理机制实现延迟加载，我们使用 Session 的 <code>load()</code> 方法加载数据或者一对多关联映射在使用延迟加载的情况下从一的一方加载多的一方，得到的都是虚拟代理，简单的说返回给用户的并不是实体本身，而是实体对象的代理。代理对象在用户调用 <code>getter</code> 方法时才会去数据库加载数据。但加载数据就需要数据库连接。而当我们把会话关闭时，数据库连接就同时关闭了。</p><p>延迟加载与 session 关闭的矛盾一般可以这样处理：</p><ol><li>关闭延迟加载特性。这种方式操作起来比较简单，因为 Hibernate 的延迟加载特性是可以通过映射文件或者注解进行配置的，但这种解决方案存在明显的缺陷。首先，出现 &quot;no session or session was closed&quot; 通常说明系统中已经存在主外键关联，如果去掉延迟加载的话，每次查询的开销都会变得很大。</li><li>在 session 关闭之前先获取需要查询的数据，可以使用工具方法 <code>Hibernate.isInitialized()</code> 判断对象是否被加载，如果没有被加载则可以使用 <code>Hibernate.initialize()</code> 方法加载对象。</li><li>使用拦截器或过滤器延长 Session 的生命周期直到视图获得数据。Spring 整合 Hibernate 提供的 <code>OpenSessionInViewFilter</code> 和 <code>OpenSessionInViewInterceptor</code> 就是这种做法。</li></ol><h2 id="举一个多对多关联的例子，并说明如何实现多对多关联映射。"><a href="#举一个多对多关联的例子，并说明如何实现多对多关联映射。" class="headerlink" title="举一个多对多关联的例子，并说明如何实现多对多关联映射。"></a>举一个多对多关联的例子，并说明如何实现多对多关联映射。</h2><p>答：例如：商品和订单、学生和课程都是典型的多对多关系。可以在实体类上通过 <code>@ManyToMany</code> 注解配置多对多关联或者通过映射文件中的和标签配置多对多关联，但是实际项目开发中，很多时候都是将多对多关联映射转换成两个多对一关联映射来实现的。</p><h2 id="谈一下你对继承映射的理解。"><a href="#谈一下你对继承映射的理解。" class="headerlink" title="谈一下你对继承映射的理解。"></a>谈一下你对继承映射的理解。</h2><p>答：继承关系的映射策略有三种：</p><ul><li>每个继承结构一张表（table per class hierarchy），不管多少个子类都用一张表。</li><li>每个子类一张表（table per subclass），公共信息放一张表，特有信息放单独的表。</li><li>每个具体类一张表（table per concrete class），有多少个子类就有多少张表。</li></ul><p>第一种方式属于单表策略，其优点在于查询子类对象的时候无需表连接，查询速度快，适合多态查询；缺点是可能导致表很大。后两种方式属于多表策略，其优点在于数据存储紧凑，其缺点是需要进行连接查询，不适合多态查询。</p><h2 id="简述-Hibernate-常见优化策略。"><a href="#简述-Hibernate-常见优化策略。" class="headerlink" title="简述 Hibernate 常见优化策略。"></a>简述 Hibernate 常见优化策略。</h2><p>答：这个问题应当挑自己使用过的优化策略回答，常用的有：</p><ul><li>制定合理的缓存策略（二级缓存、查询缓存）。</li><li>采用合理的 Session 管理机制。</li><li>尽量使用延迟加载特性。</li><li>设定合理的批处理参数。</li><li>如果可以，选用 UUID 作为主键生成器。</li><li>如果可以，选用基于版本号的乐观锁替代悲观锁。</li><li>在开发过程中, 开启 <code>hibernate.show_sql</code> 选项查看生成的 SQL，从而了解底层的状况；开发完成后关闭此选项。</li><li>考虑数据库本身的优化，合理的索引、恰当的数据分区策略等都会对持久层的性能带来可观的提升，但这些需要专业的 DBA（数据库管理员）提供支持。</li></ul><h2 id="谈一谈-Hibernate-的一级缓存、二级缓存和查询缓存。"><a href="#谈一谈-Hibernate-的一级缓存、二级缓存和查询缓存。" class="headerlink" title="谈一谈 Hibernate 的一级缓存、二级缓存和查询缓存。"></a>谈一谈 Hibernate 的一级缓存、二级缓存和查询缓存。</h2><p>答：Hibernate 的 Session 提供了一级缓存的功能，默认总是有效的，当应用程序保存持久化实体、修改持久化实体时，Session 并不会立即把这种改变提交到数据库，而是缓存在当前的 Session 中，除非显示调用了 Session 的 <code>flush()</code> 方法或通过 <code>close()</code> 方法关闭 Session。通过一级缓存，可以减少程序与数据库的交互，从而提高数据库访问性能。</p><p>SessionFactory 级别的二级缓存是全局性的，所有的 Session 可以共享这个二级缓存。不过二级缓存默认是关闭的，需要显示开启并指定需要使用哪种二级缓存实现类（可以使用第三方提供的实现）。一旦开启了二级缓存并设置了需要使用二级缓存的实体类，SessionFactory 就会缓存访问过的该实体类的每个对象，除非缓存的数据超出了指定的缓存空间。</p><p>一级缓存和二级缓存都是对整个实体进行缓存，不会缓存普通属性，如果希望对普通属性进行缓存，可以使用查询缓存。查询缓存是将 HQL 或 SQL 语句以及它们的查询结果作为键值对进行缓存，对于同样的查询可以直接从缓存中获取数据。查询缓存默认也是关闭的，需要显示开启。</p><h2 id="Hibernate-中-DetachedCriteria-类是做什么的？"><a href="#Hibernate-中-DetachedCriteria-类是做什么的？" class="headerlink" title="Hibernate 中 DetachedCriteria 类是做什么的？"></a>Hibernate 中 DetachedCriteria 类是做什么的？</h2><p>答：DetachedCriteria 和 Criteria 的用法基本上是一致的，但 Criteria 是由 Session 的 <code>createCriteria()</code> 方法创建的，也就意味着离开创建它的 Session，Criteria 就无法使用了。DetachedCriteria 不需要 Session 就可以创建（使用 <code>DetachedCriteria.forClass()</code> 方法创建），所以通常也称其为离线的 Criteria，在需要进行查询操作的时候再和 Session 绑定（调用其 <code>getExecutableCriteria(Session)</code> 方法），这也就意味着一个 DetachedCriteria 可以在需要的时候和不同的 Session 进行绑定。</p><h2 id="OneToMany-注解的-mappedBy-属性有什么作用？"><a href="#OneToMany-注解的-mappedBy-属性有什么作用？" class="headerlink" title="@OneToMany 注解的 mappedBy 属性有什么作用？"></a><code>@OneToMany</code> 注解的 <code>mappedBy</code> 属性有什么作用？</h2><p>答：<code>@OneToMany</code> 用来配置一对多关联映射，但通常情况下，一对多关联映射都由多的一方来维护关联关系，例如学生和班级，应该在学生类中添加班级属性来维持学生和班级的关联关系（在数据库中是由学生表中的外键班级编号来维护学生表和班级表的多对一关系），如果要使用双向关联，在班级类中添加一个容器属性来存放学生，并使用 <code>@OneToMany</code> 注解进行映射，此时 <code>mappedBy</code> 属性就非常重要。如果使用 XML 进行配置，可以用 <code>&lt;set&gt;</code> 标签的 <code>inverse=&quot;true&quot;</code> 设置来达到同样的效果。</p><h2 id="MyBatis-中使用-和-书写占位符有什么区别？"><a href="#MyBatis-中使用-和-书写占位符有什么区别？" class="headerlink" title="MyBatis 中使用 # 和 $ 书写占位符有什么区别？"></a><code>MyBatis</code> 中使用 <code>#</code> 和 <code>$</code> 书写占位符有什么区别？</h2><p>答：<code>#</code> 将传入的数据都当成一个字符串，会对传入的数据自动加上引号；<code>$</code> 将传入的数据直接显示生成在 SQL 中。</p><p><strong>注意：</strong>使用 <script type="math/tex">` 占位符可能会导致 SQL 注射攻击，能用 `#` 的地方就不要使用 `</script>，写 <code>order by</code> 子句的时候应该用 <code>$</code> 而不是 <code>#</code>。</p><h2 id="解释一下-MyBatis-中命名空间（namespace）的作用。"><a href="#解释一下-MyBatis-中命名空间（namespace）的作用。" class="headerlink" title="解释一下 MyBatis 中命名空间（namespace）的作用。"></a>解释一下 MyBatis 中命名空间（namespace）的作用。</h2><p>答：在大型项目中，可能存在大量的 SQL 语句，这时候为每个 SQL 语句起一个唯一的标识（ID）就变得并不容易了。为了解决这个问题，在 MyBatis 中，可以为每个映射文件起一个唯一的命名空间，这样定义在这个映射文件中的每个 SQL 语句就成了定义在这个命名空间中的一个 ID。只要我们能够保证每个命名空间中这个 ID 是唯一的，即使在不同映射文件中的语句 ID 相同，也不会再产生冲突了。</p><h2 id="MyBatis-中的动态-SQL-是什么意思？"><a href="#MyBatis-中的动态-SQL-是什么意思？" class="headerlink" title="MyBatis 中的动态 SQL 是什么意思？"></a>MyBatis 中的动态 SQL 是什么意思？</h2><p>答：对于一些复杂的查询，我们可能会指定多个查询条件，但是这些条件可能存在也可能不存在，例如在 58 同城上面找房子，我们可能会指定面积、楼层和所在位置来查找房源，也可能会指定面积、价格、户型和所在位置来查找房源，此时就需要根据用户指定的条件动态生成 SQL 语句。如果不使用持久层框架我们可能需要自己拼装 SQL 语句，还好 MyBatis 提供了动态 SQL 的功能来解决这个问题。MyBatis 中用于实现动态 SQL 的元素主要有：</p><ul><li>if</li><li>choose / when / otherwise</li><li>trim</li><li>where</li><li>set</li><li>foreach</li></ul><p>下面是映射文件的片段。</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"> <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;foo&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;Blog&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Blog&quot;</span>&gt;</span></span><br><span class="line">     select * from t_blog where 1 = 1</span><br><span class="line">     <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;title != null&quot;</span>&gt;</span></span><br><span class="line">         and title = #&#123;title&#125;</span><br><span class="line">     <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;content != null&quot;</span>&gt;</span></span><br><span class="line">         and content = #&#123;content&#125;</span><br><span class="line">     <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;owner != null&quot;</span>&gt;</span></span><br><span class="line">         and owner = #&#123;owner&#125;</span><br><span class="line">     <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>当然也可以像下面这些书写。</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;foo&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;Blog&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Blog&quot;</span>&gt;</span></span><br><span class="line">    select * from t_blog where 1 = 1 </span><br><span class="line">    <span class="tag">&lt;<span class="name">choose</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">when</span> <span class="attr">test</span>=<span class="string">&quot;title != null&quot;</span>&gt;</span></span><br><span class="line">            and title = #&#123;title&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">when</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">when</span> <span class="attr">test</span>=<span class="string">&quot;content != null&quot;</span>&gt;</span></span><br><span class="line">            and content = #&#123;content&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">when</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">otherwise</span>&gt;</span></span><br><span class="line">            and owner = &quot;owner1&quot;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">otherwise</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">choose</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>再看看下面这个例子。</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;bar&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Blog&quot;</span>&gt;</span></span><br><span class="line">    select * from t_blog where id in</span><br><span class="line">    <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;array&quot;</span> <span class="attr">index</span>=<span class="string">&quot;index&quot;</span> <span class="attr">item</span>=<span class="string">&quot;item&quot;</span> <span class="attr">open</span>=<span class="string">&quot;(&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span> <span class="attr">close</span>=<span class="string">&quot;)&quot;</span>&gt;</span></span><br><span class="line">        #&#123;item&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">foreach</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure></p><h2 id="什么是-IoC-和-DI？DI-是如何实现的？"><a href="#什么是-IoC-和-DI？DI-是如何实现的？" class="headerlink" title="什么是 IoC 和 DI？DI 是如何实现的？"></a>什么是 IoC 和 DI？DI 是如何实现的？</h2><p>答：IoC 叫控制反转，是 Inversion of Control 的缩写，DI（Dependency Injection）叫依赖注入，是对 IoC 更简单的诠释。控制反转是把传统上由程序代码直接操控的对象的调用权交给容器，通过容器来实现对象组件的装配和管理。所谓的“控制反转”就是对组件对象控制权的转移，从程序代码本身转移到了外部容器，由容器来创建对象并管理对象之间的依赖关系。 IoC 体现了好莱坞原则 - &quot;Don’t call me, we will call you&quot;。依赖注入的基本原则是应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由容器负责，查找资源的逻辑应该从应用组件的代码中抽取出来，交给容器来完成。DI 是对 IoC 更准确的描述，即组件之间的依赖关系由容器在运行期决定，形象的来说，即由容器动态的将某种依赖关系注入到组件之中。</p><p>举个例子：一个类 A 需要用到接口 B 中的方法，那么就需要为类 A 和接口 B 建立关联或依赖关系，最原始的方法是在类 A 中创建一个接口 B 的实现类 C 的实例，但这种方法需要开发人员自行维护二者的依赖关系，也就是说当依赖关系发生变动的时候需要修改代码并重新构建整个系统。如果通过一个容器来管理这些对象以及对象的依赖关系，则只需要在类 A 中定义好用于关联接口 B 的方法（构造器或 setter 方法），将类 A 和接口 B 的实现类 C 放入容器中，通过对容器的配置来实现二者的关联。</p><p>依赖注入可以通过 setter 方法注入（设值注入）、构造器注入和接口注入三种方式来实现，Spring 支持 setter 注入和构造器注入，通常使用构造器注入来注入必须的依赖关系，对于可选的依赖关系，则 setter 注入是更好的选择，setter 注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。</p><h2 id="Spring-中-Bean-的作用域有哪些？"><a href="#Spring-中-Bean-的作用域有哪些？" class="headerlink" title="Spring 中 Bean 的作用域有哪些？"></a>Spring 中 Bean 的作用域有哪些？</h2><p>答：在 Spring 的早期版本中，仅有两个作用域：<code>singleton</code> 和 <code>prototype</code>，前者表示 Bean 以单例的方式存在；后者表示每次从容器中调用 Bean 时，都会返回一个新的实例，<code>prototype</code> 通常翻译为原型。</p><p><strong>补充：</strong>设计模式中的创建型模式中也有一个原型模式，原型模式也是一个常用的模式，例如做一个室内设计软件，所有的素材都在工具箱中，而每次从工具箱中取出的都是素材对象的一个原型，可以通过对象克隆来实现原型模式。</p><p>Spring 2.x 中针对 <code>WebApplicationContext</code> 新增了 3 个作用域，分别是：<code>request</code>（每次 HTTP 请求都会创建一个新的 Bean）、<code>session</code>（同一个 HttpSession 共享同一个 Bean，不同的 HttpSession 使用不同的 Bean）和 <code>globalSession</code>（同一个全局 Session 共享一个 Bean）。</p><p><strong>说明：</strong>单例模式和原型模式都是重要的设计模式。一般情况下，无状态或状态不可变的类适合使用单例模式。在传统开发中，由于 DAO 持有 Connection 这个非线程安全对象因而没有使用单例模式；但在 Spring 环境下，所有 DAO 类对可以采用单例模式，因为 Spring 利用 AOP 和 Java API 中的 ThreadLocal 对非线程安全的对象进行了特殊处理。</p><p>ThreadLocal 为解决多线程程序的并发问题提供了一种新的思路。ThreadLocal，顾名思义是线程的一个本地化对象，当工作于多线程中的对象使用 ThreadLocal 维护变量时，ThreadLocal 为每个使用该变量的线程分配一个独立的变量副本，所以每一个线程都可以独立的改变自己的副本，而不影响其他线程所对应的副本。从线程的角度看，这个变量就像是线程的本地变量。</p><p>ThreadLocal 类非常简单好用，只有四个方法，能用上的也就是下面三个方法：</p><ul><li><code>void set(T value)</code>：设置当前线程的线程局部变量的值。</li><li><code>T get()</code>：获得当前线程所对应的线程局部变量的值。</li><li><code>void remove()</code>：删除当前线程中线程局部变量的值。</li></ul><p>ThreadLocal 是如何做到为每一个线程维护一份独立的变量副本的呢？在 ThreadLocal 类中有一个 <code>Map</code>，键为线程对象，值是其线程对应的变量的副本，自己要模拟实现一个 ThreadLocal 类其实并不困难，代码如下所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Collections;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.Map;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyThreadLocal</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;Thread, T&gt; map = Collections.synchronizedMap(<span class="keyword">new</span> HashMap&lt;Thread, T&gt;());</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(T newValue)</span> </span>&#123;</span><br><span class="line">        map.put(Thread.currentThread(), newValue);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> map.get(Thread.currentThread());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        map.remove(Thread.currentThread());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="解释一下什么叫-AOP（面向切面编程）？"><a href="#解释一下什么叫-AOP（面向切面编程）？" class="headerlink" title="解释一下什么叫 AOP（面向切面编程）？"></a>解释一下什么叫 AOP（面向切面编程）？</h2><p>答：AOP（Aspect-Oriented Programming）指一种程序设计范型，该范型以一种称为切面（aspect）的语言构造为基础，切面是一种新的模块化机制，用来描述分散在对象、类或方法中的横切关注点（crosscutting concern）。</p><h2 id="你是如何理解“横切关注”这个概念的？"><a href="#你是如何理解“横切关注”这个概念的？" class="headerlink" title="你是如何理解“横切关注”这个概念的？"></a>你是如何理解“横切关注”这个概念的？</h2><p>答：“横切关注”是会影响到整个应用程序的关注功能，它跟正常的业务逻辑是正交的，没有必然的联系，但是几乎所有的业务逻辑都会涉及到这些关注功能。通常，事务、日志、安全性等关注就是应用中的横切关注功能。</p><h2 id="你如何理解-AOP-中的连接点（Joinpoint）、切点（Pointcut）、增强（Advice）、引介（Introduction）、织入（Weaving）、切面（Aspect）这些概念？"><a href="#你如何理解-AOP-中的连接点（Joinpoint）、切点（Pointcut）、增强（Advice）、引介（Introduction）、织入（Weaving）、切面（Aspect）这些概念？" class="headerlink" title="你如何理解 AOP 中的连接点（Joinpoint）、切点（Pointcut）、增强（Advice）、引介（Introduction）、织入（Weaving）、切面（Aspect）这些概念？"></a>你如何理解 AOP 中的连接点（Joinpoint）、切点（Pointcut）、增强（Advice）、引介（Introduction）、织入（Weaving）、切面（Aspect）这些概念？</h2><p>答：</p><ul><li>连接点（Joinpoint）：程序执行的某个特定位置（如：某个方法调用前、调用后，方法抛出异常后）。一个类或一段程序代码拥有一些具有边界性质的特定点，这些代码中的特定点就是连接点。Spring 仅支持方法的连接点。</li><li>切点（Pointcut）：如果连接点相当于数据中的记录，那么切点相当于查询条件，一个切点可以匹配多个连接点。Spring AOP 的规则解析引擎负责解析切点所设定的查询条件，找到对应的连接点。</li><li>增强（Advice）：增强是织入到目标类连接点上的一段程序代码。Spring 提供的增强接口都是带方位名的，如：BeforeAdvice、AfterReturningAdvice、ThrowsAdvice 等。很多资料上将增强译为“通知”，这明显是个词不达意的翻译，让很多程序员困惑了许久。</li></ul><p><strong>说明：</strong> Advice 在国内的很多书面资料中都被翻译成&quot;通知&quot;，但是很显然这个翻译无法表达其本质，有少量的读物上将这个词翻译为&quot;增强&quot;，这个翻译是对 Advice 较为准确的诠释，我们通过 AOP 将横切关注功能加到原有的业务逻辑上，这就是对原有业务逻辑的一种增强，这种增强可以是前置增强、后置增强、返回后增强、抛异常时增强和包围型增强。</p><ul><li>引介（Introduction）：引介是一种特殊的增强，它为类添加一些属性和方法。这样，即使一个业务类原本没有实现某个接口，通过引介功能，可以动态的未该业务类添加接口的实现逻辑，让业务类成为这个接口的实现类。</li><li>织入（Weaving）：织入是将增强添加到目标类具体连接点上的过程，AOP 有三种织入方式：<ul><li>编译期织入：需要特殊的 Java 编译期（例如 AspectJ 的 ajc）；</li><li>装载期织入：要求使用特殊的类加载器，在装载类的时候对类进行增强；</li><li>运行时织入：在运行时为目标类生成代理实现增强。Spring 采用了动态代理的方式实现了运行时织入，而 AspectJ 采用了编译期织入和装载期织入的方式。</li></ul></li><li>切面（Aspect）：切面是由切点和增强（引介）组成的，它包括了对横切关注功能的定义，也包括了对连接点的定义。</li></ul><p><strong>补充：</strong>代理模式是 GoF 提出的 23 种设计模式中最为经典的模式之一，代理模式是对象的结构模式，它给某一个对象提供一个代理对象，并由代理对象控制对原对象的引用。简单的说，代理对象可以完成比原对象更多的职责，当需要为原对象添加横切关注功能时，就可以使用原对象的代理对象。我们在打开 Office 系列的 Word 文档时，如果文档中有插图，当文档刚加载时，文档中的插图都只是一个虚框占位符，等用户真正翻到某页要查看该图片时，才会真正加载这张图，这其实就是对代理模式的使用，代替真正图片的虚框就是一个虚拟代理； Hibernate 的 <code>load</code> 方法也是返回一个虚拟代理对象，等用户真正需要访问对象的属性时，才向数据库发出 SQL 语句获得真实对象。</p><p>下面用一个找枪手代考的例子演示代理模式的使用：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 参考人员接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Candidate</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 答题</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">answerTheQuestions</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 懒学生</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LazyStudent</span> <span class="keyword">implements</span> <span class="title">Candidate</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;        <span class="comment">// 姓名</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LazyStudent</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">answerTheQuestions</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 懒学生只能写出自己的名字不会答题</span></span><br><span class="line">        System.out.println(<span class="string">&quot;姓名: &quot;</span> + name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 枪手</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Gunman</span> <span class="keyword">implements</span> <span class="title">Candidate</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Candidate target;     <span class="comment">// 被代理对象</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Gunman</span><span class="params">(Candidate target)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.target = target;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">answerTheQuestions</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 枪手要写上代考的学生的姓名</span></span><br><span class="line">        target.answerTheQuestions();</span><br><span class="line">        <span class="comment">// 枪手要帮助懒学生答题并交卷</span></span><br><span class="line">        System.out.println(<span class="string">&quot;奋笔疾书正确答案&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;交卷&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProxyTest1</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Candidate c = <span class="keyword">new</span> Gunman(<span class="keyword">new</span> LazyStudent(<span class="string">&quot;王小二&quot;</span>));</span><br><span class="line">        c.answerTheQuestions();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>说明：</strong>从 JDK 1.3 开始，Java 提供了动态代理技术，允许开发者在运行时创建接口的代理实例，主要包括 <code>Proxy</code> 类和 <code>InvocationHandler</code> 接口。下面的例子使用动态代理为 <code>ArrayList</code> 编写一个代理，在添加和删除元素时，在控制台打印添加或删除的元素以及 <code>ArrayList</code> 的大小：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ListProxy</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">InvocationHandler</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> List&lt;T&gt; target;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ListProxy</span><span class="params">(List&lt;T&gt; target)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.target = target;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        Object retVal = <span class="keyword">null</span>;</span><br><span class="line">        System.out.println(<span class="string">&quot;[&quot;</span> + method.getName() + <span class="string">&quot;: &quot;</span> + args[<span class="number">0</span>] + <span class="string">&quot;]&quot;</span>);</span><br><span class="line">        retVal = method.invoke(target, args);</span><br><span class="line">        System.out.println(<span class="string">&quot;[size=&quot;</span> + target.size() + <span class="string">&quot;]&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> retVal;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProxyTest2</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        List&lt;String&gt; list = <span class="keyword">new</span> ArrayList&lt;String&gt;();</span><br><span class="line">        Class&lt;?&gt; clazz = list.getClass();</span><br><span class="line">        ListProxy&lt;String&gt; myProxy = <span class="keyword">new</span> ListProxy&lt;String&gt;(list);</span><br><span class="line">        List&lt;String&gt; newList = (List&lt;String&gt;) </span><br><span class="line">                Proxy.newProxyInstance(clazz.getClassLoader(), </span><br><span class="line">                clazz.getInterfaces(), myProxy);</span><br><span class="line">        newList.add(<span class="string">&quot;apple&quot;</span>);</span><br><span class="line">        newList.add(<span class="string">&quot;banana&quot;</span>);</span><br><span class="line">        newList.add(<span class="string">&quot;orange&quot;</span>);</span><br><span class="line">        newList.remove(<span class="string">&quot;banana&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>说明：</strong>使用 Java 的动态代理有一个局限性就是代理的类必须要实现接口，虽然面向接口编程是每个优秀的 Java 程序都知道的规则，但现实往往不尽如人意，对于没有实现接口的类如何为其生成代理呢？继承！继承是最经典的扩展已有代码能力的手段，虽然继承常常被初学者滥用，但继承也常常被进阶的程序员忽视。CGLib 采用非常底层的字节码生成技术，通过为一个类创建子类来生成代理，它弥补了 Java 动态代理的不足，因此 Spring 中动态代理和 CGLib 都是创建代理的重要手段，对于实现了接口的类就用动态代理为其生成代理类，而没有实现接口的类就用 CGLib 通过继承的方式为其创建代理。</p><h2 id="Spring-中自动装配的方式有哪些？"><a href="#Spring-中自动装配的方式有哪些？" class="headerlink" title="Spring 中自动装配的方式有哪些？"></a>Spring 中自动装配的方式有哪些？</h2><p>答：</p><ul><li><code>no</code>：不进行自动装配，手动设置 Bean 的依赖关系。</li><li><code>byName</code>：根据 Bean 的名字进行自动装配。</li><li><code>byType</code>：根据 Bean 的类型进行自动装配。</li><li><code>constructor</code>：类似于 <code>byType</code>，不过是应用于构造器的参数，如果正好有一个 Bean 与构造器的参数类型相同则可以自动装配，否则会导致错误。</li><li><code>autodetect</code>：如果有默认的构造器，则通过 <code>constructor</code> 的方式进行自动装配，否则使用 <code>byType</code> 的方式进行自动装配。</li></ul><p><strong>说明：</strong>自动装配没有自定义装配方式那么精确，而且不能自动装配简单属性（基本类型、字符串等），在使用时应注意。</p><h2 id="Spring-中如何使用注解来配置-Bean？有哪些相关的注解？"><a href="#Spring-中如何使用注解来配置-Bean？有哪些相关的注解？" class="headerlink" title="Spring 中如何使用注解来配置 Bean？有哪些相关的注解？"></a>Spring 中如何使用注解来配置 Bean？有哪些相关的注解？</h2><p>答：首先需要在 Spring 配置文件中增加如下配置：</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;org.example&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure></p><p>然后可以用 <code>@Component</code>、<code>@Controller</code>、<code>@Service</code>、<code>@Repository</code> 注解来标注需要由 Spring IoC 容器进行对象托管的类。这几个注解没有本质区别，只不过 <code>@Controller</code> 通常用于控制器，<code>@Service</code> 通常用于业务逻辑类，<code>@Repository</code> 通常用于仓储类（例如我们的 DAO 实现类），普通的类用 <code>@Component</code> 来标注。</p><h2 id="Spring-支持的事务管理类型有哪些？你在项目中使用哪种方式？"><a href="#Spring-支持的事务管理类型有哪些？你在项目中使用哪种方式？" class="headerlink" title="Spring 支持的事务管理类型有哪些？你在项目中使用哪种方式？"></a>Spring 支持的事务管理类型有哪些？你在项目中使用哪种方式？</h2><p>答：Spring 支持<strong>编程式</strong>事务管理和<strong>声明式</strong>事务管理。许多 Spring 框架的用户选择声明式事务管理，因为这种方式和应用程序的关联较少，因此更加符合轻量级容器的概念。声明式事务管理要优于编程式事务管理，尽管在灵活性方面它弱于编程式事务管理，因为编程式事务允许你通过代码控制业务。</p><p>事务分为<strong>全局事务</strong>和<strong>局部事务</strong>。全局事务由应用服务器管理，需要底层服务器 JTA 支持（如 WebLogic、WildFly 等）。局部事务和底层采用的持久化方案有关，例如使用 JDBC 进行持久化时，需要使用 Connetion 对象来操作事务；而采用 Hibernate 进行持久化时，需要使用 Session 对象来操作事务。</p><p>Spring 提供了如下所示的事务管理器。</p><div class="table-container"><table><thead><tr><th>事务管理器实现类</th><th>目标对象</th></tr></thead><tbody><tr><td>DataSourceTransactionManager</td><td>注入 DataSource</td></tr><tr><td>HibernateTransactionManager</td><td>注入 SessionFactory</td></tr><tr><td>JdoTransactionManager</td><td>管理 JDO 事务</td></tr><tr><td>JtaTransactionManager</td><td>使用 JTA 管理事务</td></tr><tr><td>PersistenceBrokerTransactionManager</td><td>管理 Apache 的 OJB 事务</td></tr></tbody></table></div><p>这些事务的父接口都是 <code>PlatformTransactionManager</code>。Spring 的事务管理机制是一种典型的策略模式，<code>PlatformTransactionManager</code> 代表事务管理接口，该接口定义了三个方法，该接口并不知道底层如何管理事务，但是它的实现类必须提供 <code>getTransaction()</code> 方法（开启事务）、<code>commit()</code> 方法（提交事务）、<code>rollback()</code> 方法（回滚事务）的多态实现，这样就可以用不同的实现类代表不同的事务管理策略。使用 JTA 全局事务策略时，需要底层应用服务器支持，而不同的应用服务器所提供的 JTA 全局事务可能存在细节上的差异，因此实际配置全局事务管理器是可能需要使用 <code>JtaTransactionManager</code> 的子类，如：<code>WebLogicJtaTransactionManager</code>（Oracle 的 WebLogic 服务器提供）、<code>UowJtaTransactionManager</code>（IBM 的 WebSphere 服务器提供）等。</p><p>编程式事务管理如下所示。</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">     <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span>  <span class="attr">xmlns:p</span>=<span class="string">&quot;http://www.springframework.org/schema/p&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:p</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">     <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">     <span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.jackfrued&quot;</span>/&gt;</span></span><br><span class="line"></span><br><span class="line">     <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;propertyConfig&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">class</span>=<span class="string">&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;location&quot;</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">value</span>&gt;</span>jdbc.properties<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">     <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.apache.commons.dbcp.BasicDataSource&quot;</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;driverClassName&quot;</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">value</span>&gt;</span>$&#123;db.driver&#125;<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;url&quot;</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">value</span>&gt;</span>$&#123;db.url&#125;<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">value</span>&gt;</span>$&#123;db.username&#125;<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">value</span>&gt;</span>$&#123;db.password&#125;<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">     <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;jdbcTemplate&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.springframework.jdbc.core.JdbcTemplate&quot;</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;dataSource&quot;</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">ref</span> <span class="attr">bean</span>=<span class="string">&quot;dataSource&quot;</span> /&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">     <span class="comment">&lt;!-- JDBC 事务管理器 --&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;transactionManager&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">class</span>=<span class="string">&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot;</span>　<span class="attr">scope</span>=<span class="string">&quot;singleton&quot;</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;dataSource&quot;</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">ref</span> <span class="attr">bean</span>=<span class="string">&quot;dataSource&quot;</span> /&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">     <span class="comment">&lt;!-- 声明事务模板 --&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;transactionTemplate&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">class</span>=<span class="string">&quot;org.springframework.transaction.support.TransactionTemplate&quot;</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;transactionManager&quot;</span>&gt;</span></span><br><span class="line">             <span class="tag">&lt;<span class="name">ref</span> <span class="attr">bean</span>=<span class="string">&quot;transactionManager&quot;</span> /&gt;</span></span><br><span class="line">         <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.dao.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.jdbc.core.JdbcTemplate;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.dao.EmpDao;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.entity.Emp;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EmpDaoImpl</span> <span class="keyword">implements</span> <span class="title">EmpDao</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JdbcTemplate jdbcTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">save</span><span class="params">(Emp emp)</span> </span>&#123;</span><br><span class="line">        String sql = <span class="string">&quot;insert into emp values (?,?,?)&quot;</span>;</span><br><span class="line">        <span class="keyword">return</span> jdbcTemplate.update(sql, emp.getId(), emp.getName(), emp.getBirthday()) == <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.biz.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Service;</span><br><span class="line"><span class="keyword">import</span> org.springframework.transaction.TransactionStatus;</span><br><span class="line"><span class="keyword">import</span> org.springframework.transaction.support.TransactionCallbackWithoutResult;</span><br><span class="line"><span class="keyword">import</span> org.springframework.transaction.support.TransactionTemplate;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.biz.EmpService;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.dao.EmpDao;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.entity.Emp;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EmpServiceImpl</span> <span class="keyword">implements</span> <span class="title">EmpService</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> TransactionTemplate txTemplate;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> EmpDao empDao;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addEmp</span><span class="params">(<span class="keyword">final</span> Emp emp)</span> </span>&#123;</span><br><span class="line">        txTemplate.execute(<span class="keyword">new</span> TransactionCallbackWithoutResult() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">doInTransactionWithoutResult</span><span class="params">(TransactionStatus txStatus)</span> </span>&#123;</span><br><span class="line">                empDao.save(emp);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>声明式事务如下图所示，以 Spring 整合 Hibernate 3 为例，包括完整的 DAO 和业务逻辑代码。</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span> </span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:p</span>=<span class="string">&quot;http://www.springframework.org/schema/p&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:aop</span>=<span class="string">&quot;http://www.springframework.org/schema/aop&quot;</span> </span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:tx</span>=<span class="string">&quot;http://www.springframework.org/schema/tx&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="tag"><span class="string">           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">           http://www.springframework.org/schema/context</span></span></span><br><span class="line"><span class="tag"><span class="string">           http://www.springframework.org/schema/context/spring-context-3.2.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">           http://www.springframework.org/schema/aop</span></span></span><br><span class="line"><span class="tag"><span class="string">           http://www.springframework.org/schema/aop/spring-aop-3.2.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">           http://www.springframework.org/schema/tx</span></span></span><br><span class="line"><span class="tag"><span class="string">           http://www.springframework.org/schema/tx/spring-tx-3.2.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 配置由 Spring IoC 容器托管的对象对应的被注解的类所在的包 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.jackfrued&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 配置通过自动生成代理实现 AOP 功能 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">aop:aspectj-autoproxy</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 配置数据库连接池 (DBCP) --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.apache.commons.dbcp.BasicDataSource&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">destroy-method</span>=<span class="string">&quot;close&quot;</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置驱动程序类 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;driverClassName&quot;</span> <span class="attr">value</span>=<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置连接数据库的 URL --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;url&quot;</span> <span class="attr">value</span>=<span class="string">&quot;jdbc:mysql://localhost:3306/myweb&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置访问数据库的用户名 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;root&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置访问数据库的口令 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;123456&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置最大连接数 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;maxActive&quot;</span> <span class="attr">value</span>=<span class="string">&quot;150&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置最小空闲连接数 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;minIdle&quot;</span> <span class="attr">value</span>=<span class="string">&quot;5&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置最大空闲连接数 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;maxIdle&quot;</span> <span class="attr">value</span>=<span class="string">&quot;20&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置初始连接数 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;initialSize&quot;</span> <span class="attr">value</span>=<span class="string">&quot;10&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置连接被泄露时是否生成日志 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;logAbandoned&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置是否删除超时连接 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;removeAbandoned&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置删除超时连接的超时门限值(以秒为单位) --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;removeAbandonedTimeout&quot;</span> <span class="attr">value</span>=<span class="string">&quot;120&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置超时等待时间(以毫秒为单位) --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;maxWait&quot;</span> <span class="attr">value</span>=<span class="string">&quot;5000&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置空闲连接回收器线程运行的时间间隔(以毫秒为单位) --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;timeBetweenEvictionRunsMillis&quot;</span> <span class="attr">value</span>=<span class="string">&quot;300000&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置连接空闲多长时间后(以毫秒为单位)被断开连接 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;minEvictableIdleTimeMillis&quot;</span> <span class="attr">value</span>=<span class="string">&quot;60000&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 配置 Spring 提供的支持注解 ORM 映射的 Hibernate 会话工厂 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;sessionFactory&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">class</span>=<span class="string">&quot;org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean&quot;</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 通过 setter 注入数据源属性 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;dataSource&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置实体类所在的包 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;packagesToScan&quot;</span> <span class="attr">value</span>=<span class="string">&quot;com.jackfrued.entity&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 配置 Hibernate 的相关属性 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;hibernateProperties&quot;</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- 在项目调试完成后要删除 show_sql 和 format_sql 属性否则对性能有显著影响 --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">value</span>&gt;</span></span><br><span class="line">                hibernate.dialect=org.hibernate.dialect.MySQL5Dialect</span><br><span class="line">            <span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 配置 Spring 提供的 Hibernate 事务管理器 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;transactionManager&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">class</span>=<span class="string">&quot;org.springframework.orm.hibernate3.HibernateTransactionManager&quot;</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 通过 setter 注入 Hibernate 会话工厂 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;sessionFactory&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;sessionFactory&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 配置基于注解配置声明式事务 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">tx:annotation-driven</span> /&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.dao;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.Serializable;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.QueryBean;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.QueryResult;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 数据访问对象接口(以对象为单位封装 CRUD 操作)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;E&gt; 实体类型</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;K&gt; 实体标识字段的类型</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">BaseDao</span> &lt;<span class="title">E</span>, <span class="title">K</span> <span class="keyword">extends</span> <span class="title">Serializable</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 新增</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> entity 业务实体对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 增加成功返回实体对象的标识</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> K <span class="title">save</span><span class="params">(E entity)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> entity 业务实体对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">delete</span><span class="params">(E entity)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 ID 删除</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id 业务实体对象的标识</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 删除成功返回 true 否则返回 false</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">deleteById</span><span class="params">(K id)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 修改</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> entity 业务实体对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 修改成功返回 true 否则返回 false</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">update</span><span class="params">(E entity)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 ID 查找业务实体对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id 业务实体对象的标识</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 业务实体对象对象或 null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> E <span class="title">findById</span><span class="params">(K id)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 ID 查找业务实体对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id 业务实体对象的标识</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> lazy 是否使用延迟加载</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 业务实体对象对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> E <span class="title">findById</span><span class="params">(K id, <span class="keyword">boolean</span> lazy)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查找所有业务实体对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 装所有业务实体对象的列表容器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;E&gt; <span class="title">findAll</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分页查找业务实体对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> page 页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> size 页面大小</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 查询结果对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryResult&lt;E&gt; <span class="title">findByPage</span><span class="params">(<span class="keyword">int</span> page, <span class="keyword">int</span> size)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分页查找业务实体对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> queryBean 查询条件对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> page 页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> size 页面大小</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 查询结果对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryResult&lt;E&gt; <span class="title">findByPage</span><span class="params">(QueryBean queryBean, <span class="keyword">int</span> page, <span class="keyword">int</span> size)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.dao;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.Serializable;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.QueryBean;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.QueryResult;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * BaseDao 的缺省适配器</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;E&gt; 实体类型</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;K&gt; 实体标识字段的类型</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseDaoAdapter</span>&lt;<span class="title">E</span>, <span class="title">K</span> <span class="keyword">extends</span> <span class="title">Serializable</span>&gt; <span class="keyword">implements</span> <span class="title">BaseDao</span>&lt;<span class="title">E</span>, <span class="title">K</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> K <span class="title">save</span><span class="params">(E entity)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">delete</span><span class="params">(E entity)</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">deleteById</span><span class="params">(K id)</span> </span>&#123;</span><br><span class="line">        E entity = findById(id);</span><br><span class="line">        <span class="keyword">if</span>(entity != <span class="keyword">null</span>) &#123;</span><br><span class="line">            delete(entity);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">update</span><span class="params">(E entity)</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> E <span class="title">findById</span><span class="params">(K id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> E <span class="title">findById</span><span class="params">(K id, <span class="keyword">boolean</span> lazy)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;E&gt; <span class="title">findAll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryResult&lt;E&gt; <span class="title">findByPage</span><span class="params">(<span class="keyword">int</span> page, <span class="keyword">int</span> size)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryResult&lt;E&gt; <span class="title">findByPage</span><span class="params">(QueryBean queryBean, <span class="keyword">int</span> page, <span class="keyword">int</span> size)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.dao;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.Serializable;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.ParameterizedType;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.Collections;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.hibernate.Query;</span><br><span class="line"><span class="keyword">import</span> org.hibernate.Session;</span><br><span class="line"><span class="keyword">import</span> org.hibernate.SessionFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.HQLQueryBean;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.QueryBean;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.QueryResult;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 基于 Hibernate 的 BaseDao 实现类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;E&gt; 实体类型</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;K&gt; 主键类型</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings(value = &#123;&quot;unchecked&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseDaoHibernateImpl</span>&lt;<span class="title">E</span>, <span class="title">K</span> <span class="keyword">extends</span> <span class="title">Serializable</span>&gt; <span class="keyword">extends</span> <span class="title">BaseDaoAdapter</span>&lt;<span class="title">E</span>, <span class="title">K</span>&gt; </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">protected</span> SessionFactory sessionFactory;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Class&lt;?&gt; entityClass;       <span class="comment">// 业务实体的类对象</span></span><br><span class="line">    <span class="keyword">private</span> String entityName;          <span class="comment">// 业务实体的名字</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BaseDaoHibernateImpl</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        ParameterizedType pt = (ParameterizedType) <span class="keyword">this</span>.getClass().getGenericSuperclass();</span><br><span class="line">        entityClass = (Class&lt;?&gt;) pt.getActualTypeArguments()[<span class="number">0</span>];</span><br><span class="line">        entityName = entityClass.getSimpleName();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> K <span class="title">save</span><span class="params">(E entity)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (K) sessionFactory.getCurrentSession().save(entity);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">delete</span><span class="params">(E entity)</span> </span>&#123;</span><br><span class="line">        sessionFactory.getCurrentSession().delete(entity);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">update</span><span class="params">(E entity)</span> </span>&#123;</span><br><span class="line">        sessionFactory.getCurrentSession().update(entity);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> E <span class="title">findById</span><span class="params">(K id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> findById(id, <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> E <span class="title">findById</span><span class="params">(K id, <span class="keyword">boolean</span> lazy)</span> </span>&#123;</span><br><span class="line">        Session session = sessionFactory.getCurrentSession();</span><br><span class="line">        <span class="keyword">return</span> (E) (lazy? session.load(entityClass, id) : session.get(entityClass, id));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;E&gt; <span class="title">findAll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> sessionFactory.getCurrentSession().createCriteria(entityClass).list();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryResult&lt;E&gt; <span class="title">findByPage</span><span class="params">(<span class="keyword">int</span> page, <span class="keyword">int</span> size)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> QueryResult&lt;E&gt;(</span><br><span class="line">            findByHQLAndPage(<span class="string">&quot;from &quot;</span> + entityName , page, size),</span><br><span class="line">            getCountByHQL(<span class="string">&quot;select count(*) from &quot;</span> + entityName)</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryResult&lt;E&gt; <span class="title">findByPage</span><span class="params">(QueryBean queryBean, <span class="keyword">int</span> page, <span class="keyword">int</span> size)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(queryBean <span class="keyword">instanceof</span> HQLQueryBean) &#123;</span><br><span class="line">            HQLQueryBean hqlQueryBean = (HQLQueryBean) queryBean;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> QueryResult&lt;E&gt;(</span><br><span class="line">                findByHQLAndPage(hqlQueryBean.getQueryString(), page, size, hqlQueryBean.getParameters()),</span><br><span class="line">                getCountByHQL(hqlQueryBean.getCountString(), hqlQueryBean.getParameters())</span><br><span class="line">            );</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 HQL 和可变参数列表进行查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> hql 基于 HQL 的查询语句</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> params 可变参数列表</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 持有查询结果的列表容器或空列表容器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> List&lt;E&gt; <span class="title">findByHQL</span><span class="params">(String hql, Object... params)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.findByHQL(hql, getParamList(params));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 HQL 和参数列表进行查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> hql 基于 HQL 的查询语句</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> params 查询参数列表</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 持有查询结果的列表容器或空列表容器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> List&lt;E&gt; <span class="title">findByHQL</span><span class="params">(String hql, List&lt;Object&gt; params)</span> </span>&#123;</span><br><span class="line">        List&lt;E&gt; list = createQuery(hql, params).list();</span><br><span class="line">        <span class="keyword">return</span> list != <span class="keyword">null</span> &amp;&amp; list.size() &gt; <span class="number">0</span> ? list : Collections.EMPTY_LIST;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 HQL 和参数列表进行分页查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> hql 基于 HQL 的查询语句</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@page</span> 页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@size</span> 页面大小</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> params 可变参数列表</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 持有查询结果的列表容器或空列表容器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> List&lt;E&gt; <span class="title">findByHQLAndPage</span><span class="params">(String hql, <span class="keyword">int</span> page, <span class="keyword">int</span> size, Object... params)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.findByHQLAndPage(hql, page, size, getParamList(params));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据 HQL 和参数列表进行分页查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> hql 基于 HQL 的查询语句</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@page</span> 页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@size</span> 页面大小</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> params 查询参数列表</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 持有查询结果的列表容器或空列表容器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> List&lt;E&gt; <span class="title">findByHQLAndPage</span><span class="params">(String hql, <span class="keyword">int</span> page, <span class="keyword">int</span> size, List&lt;Object&gt; params)</span> </span>&#123;</span><br><span class="line">        List&lt;E&gt; list = createQuery(hql, params)</span><br><span class="line">                .setFirstResult((page - <span class="number">1</span>) * size)</span><br><span class="line">                .setMaxResults(size)</span><br><span class="line">                .list();</span><br><span class="line">        <span class="keyword">return</span> list != <span class="keyword">null</span> &amp;&amp; list.size() &gt; <span class="number">0</span> ? list : Collections.EMPTY_LIST;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查询满足条件的记录数</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> hql 基于 HQL 的查询语句</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> params 可变参数列表</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 满足查询条件的总记录数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">long</span> <span class="title">getCountByHQL</span><span class="params">(String hql, Object... params)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.getCountByHQL(hql, getParamList(params));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查询满足条件的记录数</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> hql 基于 HQL 的查询语句</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> params 参数列表容器</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 满足查询条件的总记录数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">long</span> <span class="title">getCountByHQL</span><span class="params">(String hql, List&lt;Object&gt; params)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (Long) createQuery(hql, params).uniqueResult();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 Hibernate 查询对象(Query)</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> Query <span class="title">createQuery</span><span class="params">(String hql, List&lt;Object&gt; params)</span> </span>&#123;</span><br><span class="line">        Query query = sessionFactory.getCurrentSession().createQuery(hql);</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; params.size(); i++) &#123;</span><br><span class="line">            query.setParameter(i, params.get(i));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> query;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将可变参数列表组装成列表容器</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> List&lt;Object&gt; <span class="title">getParamList</span><span class="params">(Object... params)</span> </span>&#123;</span><br><span class="line">        List&lt;Object&gt; paramList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">        <span class="keyword">if</span>(params != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; params.length; i++) &#123;</span><br><span class="line">                paramList.add(params[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> paramList.size() == <span class="number">0</span>? Collections.EMPTY_LIST : paramList;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.comm;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 查询条件的接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">QueryBean</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加排序字段</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> fieldName 用于排序的字段</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> asc 升序还是降序</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 查询条件对象自身(方便级联编程)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryBean <span class="title">addOrder</span><span class="params">(String fieldName, <span class="keyword">boolean</span> asc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加排序字段</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> available 是否添加此排序字段</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> fieldName 用于排序的字段</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> asc 升序还是降序</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 查询条件对象自身(方便级联编程)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryBean <span class="title">addOrder</span><span class="params">(<span class="keyword">boolean</span> available, String fieldName, <span class="keyword">boolean</span> asc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加查询条件</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> condition 条件</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> params 替换掉条件中参数占位符的参数</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 查询条件对象自身(方便级联编程)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryBean <span class="title">addCondition</span><span class="params">(String condition, Object... params)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加查询条件</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> available 是否需要添加此条件</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> condition 条件</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> params 替换掉条件中参数占位符的参数</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 查询条件对象自身(方便级联编程)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryBean <span class="title">addCondition</span><span class="params">(<span class="keyword">boolean</span> available, String condition, Object... params)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获得查询语句</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 查询语句</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getQueryString</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取查询记录数的查询语句</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 查询记录数的查询语句</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getCountString</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获得查询参数</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 查询参数的列表容器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;Object&gt; <span class="title">getParameters</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.comm;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 查询结果</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;T&gt; 泛型参数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">QueryResult</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> List&lt;T&gt; result;     <span class="comment">// 持有查询结果的列表容器</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">long</span> totalRecords;  <span class="comment">// 查询到的总记录数</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构造器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">QueryResult</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构造器</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> result 持有查询结果的列表容器</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> totalRecords 查询到的总记录数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">QueryResult</span><span class="params">(List&lt;T&gt; result, <span class="keyword">long</span> totalRecords)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.result = result;</span><br><span class="line">        <span class="keyword">this</span>.totalRecords = totalRecords;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;T&gt; <span class="title">getResult</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setResult</span><span class="params">(List&lt;T&gt; result)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.result = result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">getTotalRecords</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> totalRecords;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setTotalRecords</span><span class="params">(<span class="keyword">long</span> totalRecords)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.totalRecords = totalRecords;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.dao;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.QueryResult;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.entity.Dept;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 部门数据访问对象接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">DeptDao</span> <span class="keyword">extends</span> <span class="title">BaseDao</span>&lt;<span class="title">Dept</span>, <span class="title">Integer</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分页查询顶级部门</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> page 页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> size 页码大小</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 查询结果对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryResult&lt;Dept&gt; <span class="title">findTopDeptByPage</span><span class="params">(<span class="keyword">int</span> page, <span class="keyword">int</span> size)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.dao.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Repository;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.QueryResult;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.dao.BaseDaoHibernateImpl;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.dao.DeptDao;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.entity.Dept;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DeptDaoImpl</span> <span class="keyword">extends</span> <span class="title">BaseDaoHibernateImpl</span>&lt;<span class="title">Dept</span>, <span class="title">Integer</span>&gt; <span class="keyword">implements</span> <span class="title">DeptDao</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String HQL_FIND_TOP_DEPT = <span class="string">&quot; from Dept as d where d.superiorDept is null &quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> QueryResult&lt;Dept&gt; <span class="title">findTopDeptByPage</span><span class="params">(<span class="keyword">int</span> page, <span class="keyword">int</span> size)</span> </span>&#123;</span><br><span class="line">        List&lt;Dept&gt; list = findByHQLAndPage(HQL_FIND_TOP_DEPT, page, size);</span><br><span class="line">        <span class="keyword">long</span> totalRecords = getCountByHQL(<span class="string">&quot; select count(*) &quot;</span> + HQL_FIND_TOP_DEPT);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> QueryResult&lt;&gt;(list, totalRecords);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.comm;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 分页器</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;T&gt; 分页数据对象的类型</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PageBean</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAUL_INIT_PAGE = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_PAGE_SIZE = <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_PAGE_COUNT = <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;T&gt; data;           <span class="comment">// 分页数据</span></span><br><span class="line">    <span class="keyword">private</span> PageRange pageRange;    <span class="comment">// 页码范围</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> totalPage;          <span class="comment">// 总页数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> size;               <span class="comment">// 页面大小</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> currentPage;        <span class="comment">// 当前页码</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> pageCount;          <span class="comment">// 页码数量</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构造器</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> currentPage 当前页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> size 页码大小</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> pageCount 页码数量</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">PageBean</span><span class="params">(<span class="keyword">int</span> currentPage, <span class="keyword">int</span> size, <span class="keyword">int</span> pageCount)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.currentPage = currentPage &gt; <span class="number">0</span> ? currentPage : <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">this</span>.size = size &gt; <span class="number">0</span> ? size : DEFAULT_PAGE_SIZE;</span><br><span class="line">        <span class="keyword">this</span>.pageCount = pageCount &gt; <span class="number">0</span> ? size : DEFAULT_PAGE_COUNT;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构造器</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> currentPage 当前页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> size 页码大小</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">PageBean</span><span class="params">(<span class="keyword">int</span> currentPage, <span class="keyword">int</span> size)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(currentPage, size, DEFAULT_PAGE_COUNT);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构造器</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> currentPage 当前页码</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">PageBean</span><span class="params">(<span class="keyword">int</span> currentPage)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(currentPage, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_COUNT);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构造器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">PageBean</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(DEFAUL_INIT_PAGE, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_COUNT);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;T&gt; <span class="title">getData</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> data;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getStartPage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> pageRange != <span class="keyword">null</span> ? pageRange.getStartPage() : <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getEndPage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> pageRange != <span class="keyword">null</span> ? pageRange.getEndPage() : <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">getTotalPage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> totalPage;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getSize</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> size;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getCurrentPage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> currentPage;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 将查询结果转换为分页数据</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> queryResult 查询结果对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">transferQueryResult</span><span class="params">(QueryResult&lt;T&gt; queryResult)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> totalRecords = queryResult.getTotalRecords();</span><br><span class="line"></span><br><span class="line">        data = queryResult.getResult();</span><br><span class="line">        totalPage = (<span class="keyword">int</span>) ((totalRecords + size - <span class="number">1</span>) / size); </span><br><span class="line">        totalPage = totalPage &gt;= <span class="number">0</span> ? totalPage : Integer.MAX_VALUE;</span><br><span class="line">        <span class="keyword">this</span>.pageRange = <span class="keyword">new</span> PageRange(pageCount, currentPage, totalPage);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.comm;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 页码范围</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PageRange</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> startPage;  <span class="comment">// 起始页码</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> endPage;    <span class="comment">// 终止页码</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构造器</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> pageCount 总共显示几个页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> currentPage 当前页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> totalPage 总页数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">PageRange</span><span class="params">(<span class="keyword">int</span> pageCount, <span class="keyword">int</span> currentPage, <span class="keyword">int</span> totalPage)</span> </span>&#123;</span><br><span class="line">        startPage = currentPage - (pageCount - <span class="number">1</span>) / <span class="number">2</span>;</span><br><span class="line">        endPage = currentPage + pageCount / <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">if</span>(startPage &lt; <span class="number">1</span>) &#123;</span><br><span class="line">            startPage = <span class="number">1</span>;</span><br><span class="line">            endPage = totalPage &gt; pageCount ? pageCount : totalPage;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (endPage &gt; totalPage) &#123;</span><br><span class="line">            endPage = totalPage;</span><br><span class="line">            startPage = (endPage - pageCount &gt; <span class="number">0</span>) ? endPage - pageCount + <span class="number">1</span> : <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获得起始页页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 起始页页码</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getStartPage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> startPage;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获得终止页页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 终止页页码</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getEndPage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> endPage;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.biz;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.PageBean;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.entity.Dept;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 部门业务逻辑接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">DeptService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建新的部门</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> department 部门对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 创建成功返回 true 否则返回 false</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">createNewDepartment</span><span class="params">(Dept department)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除指定部门</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id 要删除的部门的编号</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 删除成功返回 true 否则返回 false</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">deleteDepartment</span><span class="params">(Integer id)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分页获取顶级部门</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> page 页码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> size 页码大小</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 部门对象的分页器对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> PageBean&lt;Dept&gt; <span class="title">getTopDeptByPage</span><span class="params">(<span class="keyword">int</span> page, <span class="keyword">int</span> size)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.biz.impl;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Service;</span><br><span class="line"><span class="keyword">import</span> org.springframework.transaction.annotation.Transactional;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.biz.DeptService;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.PageBean;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.comm.QueryResult;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.dao.DeptDao;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.entity.Dept;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="meta">@Transactional</span>  <span class="comment">// 声明式事务的注解</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DeptServiceImpl</span> <span class="keyword">implements</span> <span class="title">DeptService</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DeptDao deptDao;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">createNewDepartment</span><span class="params">(Dept department)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> deptDao.save(department) != <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">deleteDepartment</span><span class="params">(Integer id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> deptDao.deleteById(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> PageBean&lt;Dept&gt; <span class="title">getTopDeptByPage</span><span class="params">(<span class="keyword">int</span> page, <span class="keyword">int</span> size)</span> </span>&#123;</span><br><span class="line">        QueryResult&lt;Dept&gt; queryResult = deptDao.findTopDeptByPage(page, size);</span><br><span class="line">        PageBean&lt;Dept&gt; pageBean = <span class="keyword">new</span> PageBean&lt;&gt;(page, size);</span><br><span class="line">        pageBean.transferQueryResult(queryResult);</span><br><span class="line">        <span class="keyword">return</span> pageBean;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="如何在-Web-项目中配置-Spring-的-IoC-容器？"><a href="#如何在-Web-项目中配置-Spring-的-IoC-容器？" class="headerlink" title="如何在 Web 项目中配置 Spring 的 IoC 容器？"></a>如何在 Web 项目中配置 Spring 的 IoC 容器？</h2><p>答：如果需要在 Web 项目中使用 Spring 的 IoC 容器，可以在 Web 项目配置文件 web.xml 中做出如下配置：</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context-param</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">param-name</span>&gt;</span>contextConfigLocation<span class="tag">&lt;/<span class="name">param-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">param-value</span>&gt;</span>classpath:applicationContext.xml<span class="tag">&lt;/<span class="name">param-value</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">context-param</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">listener</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">listener-class</span>&gt;</span></span><br><span class="line">        org.springframework.web.context.ContextLoaderListener</span><br><span class="line">    <span class="tag">&lt;/<span class="name">listener-class</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">listener</span>&gt;</span></span><br></pre></td></tr></table></figure></p><h2 id="如何在-Web-项目中配置-Spring-MVC？"><a href="#如何在-Web-项目中配置-Spring-MVC？" class="headerlink" title="如何在 Web 项目中配置 Spring MVC？"></a>如何在 Web 项目中配置 Spring MVC？</h2><p>答：要使用 Spring MVC 需要在 Web 项目配置文件中配置其前端控制器 <code>DispatcherServlet</code>，如下所示：</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">web-app</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>example<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-class</span>&gt;</span></span><br><span class="line">            org.springframework.web.servlet.DispatcherServlet</span><br><span class="line">        <span class="tag">&lt;/<span class="name">servlet-class</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">load-on-startup</span>&gt;</span>1<span class="tag">&lt;/<span class="name">load-on-startup</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servlet</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet-mapping</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>example<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>*.html<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servlet-mapping</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">web-app</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p><strong>说明：</strong>上面的配置中使用了 <code>*.html</code> 的后缀映射，这样做一方面不能够通过 URL 推断采用了何种服务器端的技术，另一方面可以欺骗搜索引擎，因为搜索引擎不会搜索动态页面，这种做法称为<strong>伪静态化</strong>。</p><h2 id="Spring-MVC-的工作原理是怎样的？"><a href="#Spring-MVC-的工作原理是怎样的？" class="headerlink" title="Spring MVC 的工作原理是怎样的？"></a>Spring MVC 的工作原理是怎样的？</h2><p>答：Spring MVC 的工作原理如下图所示：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/156.png" alt="Spring MVC 工作原理"></p><ol><li>客户端的所有请求都交给前端控制器 <code>DispatcherServlet</code> 来处理，它会负责调用系统的其他模块来真正处理用户的请求。</li><li><code>DispatcherServlet</code> 收到请求后，将根据请求的信息（包括 URL、HTTP 协议方法、请求头、请求参数、Cookie 等）以及 <code>HandlerMapping</code> 的配置找到处理该请求的 <code>Handler</code>（任何一个对象都可以作为请求的 <code>Handler</code>）。</li><li>在这个地方 Spring 会通过 <code>Controller</code> 对该处理器进行封装。</li><li><code>Controller</code> 是一个适配器，它用统一的接口对各种 <code>Handler</code> 中的方法进行调用。</li><li><code>Handler</code> 完成对用户请求的处理后，会返回一个 <code>ModelAndView</code> 对象给 <code>DispatcherServlet</code>，<code>ModelAndView</code> 顾名思义，包含了数据模型以及相应的视图的信息。</li><li><code>ModelAndView</code> 的视图是逻辑视图，<code>DispatcherServlet</code> 还要借助 <code>ViewResolver</code> 完成从逻辑视图到真实视图对象的解析工作。</li><li>当得到真正的视图对象后，<code>DispatcherServlet</code> 会利用视图对象对模型数据进行渲染。</li><li>客户端得到响应，可能是一个普通的 HTML 页面，也可以是 XML 或 JSON 字符串，还可以是一张图片或者一个 PDF 文件。</li></ol><h2 id="如何在-Spring-IoC-容器中配置数据源？"><a href="#如何在-Spring-IoC-容器中配置数据源？" class="headerlink" title="如何在 Spring IoC 容器中配置数据源？"></a>如何在 Spring IoC 容器中配置数据源？</h2><p>答：</p><p>DBCP 配置：</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.apache.commons.dbcp.BasicDataSource&quot;</span> <span class="attr">destroy-method</span>=<span class="string">&quot;close&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;driverClassName&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.driverClassName&#125;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;url&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.url&#125;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.username&#125;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.password&#125;&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;jdbc.properties&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure></p><p>C3P0 配置：</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.mchange.v2.c3p0.ComboPooledDataSource&quot;</span> <span class="attr">destroy-method</span>=<span class="string">&quot;close&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;driverClass&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.driverClassName&#125;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;jdbcUrl&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.url&#125;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;user&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.username&#125;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.password&#125;&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;jdbc.properties&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong> DBCP 的详细配置在第 153 题中已经完整的展示过了。</p><h2 id="如何配置配置事务增强？"><a href="#如何配置配置事务增强？" class="headerlink" title="如何配置配置事务增强？"></a>如何配置配置事务增强？</h2><p>答：</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:aop</span>=<span class="string">&quot;http://www.springframework.org/schema/aop&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:tx</span>=<span class="string">&quot;http://www.springframework.org/schema/tx&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;</span></span></span><br><span class="line"><span class="tag"><span class="string">     http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="tag"><span class="string">     http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">     http://www.springframework.org/schema/tx</span></span></span><br><span class="line"><span class="tag"><span class="string">     http://www.springframework.org/schema/tx/spring-tx.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">     http://www.springframework.org/schema/aop</span></span></span><br><span class="line"><span class="tag"><span class="string">     http://www.springframework.org/schema/aop/spring-aop.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- this is the service object that we want to make transactional --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;fooService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;x.y.service.DefaultFooService&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- the transactional advice --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">tx:advice</span> <span class="attr">id</span>=<span class="string">&quot;txAdvice&quot;</span> <span class="attr">transaction-manager</span>=<span class="string">&quot;txManager&quot;</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- the transactional semantics... --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">tx:attributes</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- all methods starting with &#x27;get&#x27; are read-only --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;get*&quot;</span> <span class="attr">read-only</span>=<span class="string">&quot;true&quot;</span> /&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- other methods use the default transaction settings (see below) --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">tx:method</span> <span class="attr">name</span>=<span class="string">&quot;*&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">tx:attributes</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">tx:advice</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- ensure that the above transactional advice runs for any execution of </span></span><br><span class="line"><span class="comment">        an operation defined by the FooService interface --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">aop:config</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">aop:pointcut</span> <span class="attr">id</span>=<span class="string">&quot;fooServiceOperation&quot;</span> <span class="attr">expression</span>=<span class="string">&quot;execution(* x.y.service.FooService.*(..))&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">aop:advisor</span> <span class="attr">advice-ref</span>=<span class="string">&quot;txAdvice&quot;</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;fooServiceOperation&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">aop:config</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- don&#x27;t forget the DataSource --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.apache.commons.dbcp.BasicDataSource&quot;</span> <span class="attr">destroy-method</span>=<span class="string">&quot;close&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;driverClassName&quot;</span> <span class="attr">value</span>=<span class="string">&quot;oracle.jdbc.driver.OracleDriver&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;url&quot;</span> <span class="attr">value</span>=<span class="string">&quot;jdbc:oracle:thin:@localhost:1521:orcl&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;scott&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;tiger&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- similarly, don&#x27;t forget the PlatformTransactionManager --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;txManager&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;dataSource&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- other &lt;bean/&gt; definitions here --&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></p><h2 id="选择使用-Spring-框架的原因（Spring-框架为企业级开发带来的好处有哪些）？"><a href="#选择使用-Spring-框架的原因（Spring-框架为企业级开发带来的好处有哪些）？" class="headerlink" title="选择使用 Spring 框架的原因（Spring 框架为企业级开发带来的好处有哪些）？"></a>选择使用 Spring 框架的原因（Spring 框架为企业级开发带来的好处有哪些）？</h2><p>答：可以从以下几个方面作答：</p><ul><li>非侵入式：支持基于 POJO 的编程模式，不强制性的要求实现 Spring 框架中的接口或继承 Spring 框架中的类。</li><li>IoC 容器：IoC 容器帮助应用程序管理对象以及对象之间的依赖关系，对象之间的依赖关系如果发生了改变只需要修改配置文件而不是修改代码，因为代码的修改可能意味着项目的重新构建和完整的回归测试。有了 IoC 容器，程序员再也不需要自己编写工厂、单例，这一点特别符合 Spring 的精神“不要重复的发明轮子”。</li><li>AOP（面向切面编程）：将所有的横切关注功能封装到切面（aspect）中，通过配置的方式将横切关注功能动态添加到目标代码上，进一步实现了业务逻辑和系统服务之间的分离。另一方面，有了 AOP 程序员可以省去很多自己写代理类的工作。</li><li>MVC：Spring 的 MVC 框架是非常优秀的，从各个方面都可以甩 Struts 2 几条街，为 Web 表示层提供了更好的解决方案。</li><li>事务管理：Spring 以宽广的胸怀接纳多种持久层技术，并且为其提供了声明式的事务管理，在不需要任何一行代码的情况下就能够完成事务管理。</li><li>其他：选择 Spring 框架的原因还远不止于此，Spring 为 Java 企业级开发提供了一站式选择，你可以在需要的时候使用它的部分和全部，更重要的是，你甚至可以在感觉不到 Spring 存在的情况下，在你的项目中使用 Spring 提供的各种优秀的功能。</li></ul><h2 id="Spring-IoC-容器配置-Bean-的方式？"><a href="#Spring-IoC-容器配置-Bean-的方式？" class="headerlink" title="Spring IoC 容器配置 Bean 的方式？"></a>Spring IoC 容器配置 Bean 的方式？</h2><p>答：</p><ul><li>基于 XML 文件进行配置。</li><li>基于注解进行配置。</li><li>基于 Java 程序进行配置（Spring 3+）</li></ul><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.bean;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> age;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> Car car;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Person</span><span class="params">(String name, <span class="keyword">int</span> age)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setCar</span><span class="params">(Car car)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.car = car;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Person [name=&quot;</span> + name + <span class="string">&quot;, age=&quot;</span> + age + <span class="string">&quot;, car=&quot;</span> + car + <span class="string">&quot;]&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.bean;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Car</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String brand;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> maxSpeed;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Car</span><span class="params">(String brand, <span class="keyword">int</span> maxSpeed)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.brand = brand;</span><br><span class="line">        <span class="keyword">this</span>.maxSpeed = maxSpeed;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Car [brand=&quot;</span> + brand + <span class="string">&quot;, maxSpeed=&quot;</span> + maxSpeed + <span class="string">&quot;]&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.config;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.bean.Car;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.bean.Person;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AppConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Car <span class="title">car</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Car(<span class="string">&quot;Benz&quot;</span>, <span class="number">320</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Person <span class="title">person</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Person(<span class="string">&quot;Jack&quot;</span>, <span class="number">34</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ConfigurableApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.AnnotationConfigApplicationContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.jackfrued.bean.Person;</span><br><span class="line"><span class="keyword">import</span> com.jackfrued.config.AppConfig;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// TWR (Java 7+)</span></span><br><span class="line">        <span class="keyword">try</span>(ConfigurableApplicationContext factory = <span class="keyword">new</span> AnnotationConfigApplicationContext(AppConfig.class)) &#123;</span><br><span class="line">            Person person = factory.getBean(Person.class);</span><br><span class="line">            System.out.println(person);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="阐述-Spring-框架中-Bean-的生命周期？"><a href="#阐述-Spring-框架中-Bean-的生命周期？" class="headerlink" title="阐述 Spring 框架中 Bean 的生命周期？"></a>阐述 Spring 框架中 Bean 的生命周期？</h2><p>答：</p><ol><li>Spring IoC 容器找到关于 Bean 的定义并实例化该 Bean。</li><li>Spring IoC 容器对 Bean 进行依赖注入。</li><li>如果 Bean 实现了 <code>BeanNameAware</code> 接口，则将该 Bean 的 <code>id</code> 传给 <code>setBeanName</code> 方法。</li><li>如果 Bean 实现了 <code>BeanFactoryAware</code> 接口，则将 <code>BeanFactory</code> 对象传给 <code>setBeanFactory</code> 方法。</li><li>如果 Bean 实现了 <code>BeanPostProcessor</code> 接口，则调用其 <code>postProcessBeforeInitialization</code> 方法。</li><li>如果 Bean 实现了 <code>InitializingBean</code> 接口，则调用其 <code>afterPropertySet</code> 方法。</li><li>如果有和 Bean 关联的 <code>BeanPostProcessors</code> 对象，则这些对象的 <code>postProcessAfterInitialization</code> 方法被调用。</li><li>当销毁 Bean 实例时，如果 Bean 实现了 <code>DisposableBean</code> 接口，则调用其 <code>destroy</code> 方法。</li></ol><h2 id="依赖注入时如何注入集合属性？"><a href="#依赖注入时如何注入集合属性？" class="headerlink" title="依赖注入时如何注入集合属性？"></a>依赖注入时如何注入集合属性？</h2><p>答：可以在定义 Bean 属性时，通过 <code>&lt;list&gt;</code>/<code>&lt;set&gt;</code>/<code>&lt;map&gt;</code>/<code>&lt;props&gt;</code> 分别为其注入列表、集合、映射和键值都是字符串的映射属性。</p><h2 id="Spring-中的自动装配有哪些限制？"><a href="#Spring-中的自动装配有哪些限制？" class="headerlink" title="Spring 中的自动装配有哪些限制？"></a>Spring 中的自动装配有哪些限制？</h2><p>答：</p><ul><li>如果使用了构造器注入或者 setter 注入，那么将覆盖自动装配的依赖关系。</li><li>基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。</li><li>优先考虑使用显式的装配来进行更精确的依赖注入而不是使用自动装配。</li></ul><h2 id="在-Web-项目中如何获得-Spring-的-IoC-容器？"><a href="#在-Web-项目中如何获得-Spring-的-IoC-容器？" class="headerlink" title="在 Web 项目中如何获得 Spring 的 IoC 容器？"></a>在 Web 项目中如何获得 Spring 的 IoC 容器？</h2><p>答：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);</span><br></pre></td></tr></table></figure></p><h2 id="大型网站在架构上应当考虑哪些问题？"><a href="#大型网站在架构上应当考虑哪些问题？" class="headerlink" title="大型网站在架构上应当考虑哪些问题？"></a>大型网站在架构上应当考虑哪些问题？</h2><p>答：</p><ul><li><p>分层：分层是处理任何复杂系统最常见的手段之一，将系统横向切分成若干个层面，每个层面只承担单一的职责，然后通过下层为上层提供的基础设施和服务以及上层对下层的调用来形成一个完整的复杂的系统。计算机网络的开放系统互联参考模型（OSI/RM）和 Internet 的 TCP/IP 模型都是分层结构。</p><p>  大型网站的软件系统也可以使用分层的理念将其分为<strong>持久层</strong>（提供数据存储和访问服务）、<strong>业务层</strong>（处理业务逻辑，系统中最核心的部分）和<strong>表示层</strong>（系统交互、视图展示）。需要指出的是：(1)分层是逻辑上的划分，在物理上可以位于同一设备上也可以在不同的设备上部署不同的功能模块，这样可以使用更多的计算资源来应对用户的并发访问；(2)层与层之间应当有清晰的边界，这样分层才有意义，才更利于软件的开发和维护。</p></li><li><p>分割：分割是对软件的纵向切分。我们可以将大型网站的不同功能和服务分割开，形成高内聚低耦合的功能模块（单元）。在设计初期可以做一个粗粒度的分割，将网站分割为若干个功能模块，后期还可以进一步对每个模块进行细粒度的分割，这样一方面有助于软件的开发和维护，另一方面有助于分布式的部署，提供网站的并发处理能力和功能的扩展。</p></li><li><p>分布式：除了上面提到的内容，网站的静态资源（JavaScript、CSS、图片等）也可以采用独立分布式部署并采用独立的域名，这样可以减轻应用服务器的负载压力，也使得浏览器对资源的加载更快。数据的存取也应该是分布式的，传统的商业级关系型数据库产品基本上都支持分布式部署，而新生的 NoSQL 产品几乎都是分布式的。当然，网站后台的业务处理也要使用分布式技术，例如查询索引的构建、数据分析等，这些业务计算规模庞大，可以使用 Hadoop 以及 MapReduce 分布式计算框架来处理。</p></li><li><p>集群：集群使得有更多的服务器提供相同的服务，可以更好的提供对并发的支持。</p></li><li><p>缓存：所谓缓存就是用空间换取时间的技术，将数据尽可能放在距离计算最近的位置。使用缓存是网站优化的第一定律。我们通常说的 CDN、反向代理、热点数据都是对缓存技术的使用。</p></li><li><p>异步：异步是实现软件实体之间解耦合的又一重要手段。异步架构是典型的生产者消费者模式，二者之间没有直接的调用关系，只要保持数据结构不变，彼此功能实现可以随意变化而不互相影响，这对网站的扩展非常有利。使用异步处理还可以提高系统可用性，加快网站的响应速度（用 Ajax 加载数据就是一种异步技术），同时还可以起到削峰作用（应对瞬时高并发）。“能推迟处理的都要推迟处理”是网站优化的第二定律，而异步是践行网站优化第二定律的重要手段。</p></li><li><p>冗余：各种服务器都要提供相应的冗余服务器以便在某台或某些服务器宕机时还能保证网站可以正常工作，同时也提供了灾难恢复的可能性。冗余是网站高可用性的重要保证。</p></li></ul><h2 id="你用过的网站前端优化的技术有哪些？"><a href="#你用过的网站前端优化的技术有哪些？" class="headerlink" title="你用过的网站前端优化的技术有哪些？"></a>你用过的网站前端优化的技术有哪些？</h2><p>答：</p><ul><li><p>浏览器访问优化：</p><ul><li>减少 HTTP 请求数量：合并 CSS、合并 JavaScript、合并图片（CSS Sprite）</li><li>使用浏览器缓存：通过设置 HTTP 响应头中的 Cache-Control 和 Expires 属性，将 CSS、JavaScript、图片等在浏览器中缓存，当这些静态资源需要更新时，可以更新 HTML 文件中的引用来让浏览器重新请求新的资源</li><li>启用压缩</li><li>CSS 前置，JavaScript 后置</li><li>减少 Cookie 传输</li></ul></li><li><p>CDN 加速：CDN（Content Distribute Network）的本质仍然是缓存，将数据缓存在离用户最近的地方，CDN 通常部署在网络运营商的机房，不仅可以提升响应速度，还可以减少应用服务器的压力。当然，CDN 缓存的通常都是静态资源。</p></li><li><p>反向代理：反向代理相当于应用服务器的一个门面，可以保护网站的安全性，也可以实现负载均衡的功能，当然最重要的是它缓存了用户访问的热点资源，可以直接从反向代理将某些内容返回给用户浏览器。</p></li></ul><h2 id="你使用过的应用服务器优化技术有哪些？"><a href="#你使用过的应用服务器优化技术有哪些？" class="headerlink" title="你使用过的应用服务器优化技术有哪些？"></a>你使用过的应用服务器优化技术有哪些？</h2><p>答：</p><ul><li><p>分布式缓存：缓存的本质就是内存中的哈希表，如果设计一个优质的哈希函数，那么理论上哈希表读写的渐近时间复杂度为 O(1)。缓存主要用来存放那些读写比很高、变化很少的数据，这样应用程序读取数据时先到缓存中读取，如果没有或者数据已经失效再去访问数据库或文件系统，并根据拟定的规则将数据写入缓存。对网站数据的访问也符合二八定律（Pareto 分布，幂律分布），即 80% 的访问都集中在 20% 的数据上，如果能够将这 20% 的数据缓存起来，那么系统的性能将得到显著的改善。当然，使用缓存需要解决以下几个问题：</p><ul><li>频繁修改的数据；</li><li>数据不一致与脏读；</li><li>缓存雪崩（可以采用分布式缓存服务器集群加以解决，<a href="http://memcached.org/">memcached</a> 是广泛采用的解决方案）；</li><li>缓存预热；</li><li>缓存穿透（恶意持续请求不存在的数据）。</li></ul></li><li><p>异步操作：可以使用消息队列将调用异步化，通过异步处理将短时间高并发产生的事件消息存储在消息队列中，从而起到削峰作用。电商网站在进行促销活动时，可以将用户的订单请求存入消息队列，这样可以抵御大量的并发订单请求对系统和数据库的冲击。目前，绝大多数的电商网站即便不进行促销活动，订单系统都采用了消息队列来处理。</p></li><li><p>使用集群。</p></li><li><p>代码优化：</p><ul><li>多线程：基于 Java 的 Web 开发基本上都通过多线程的方式响应用户的并发请求，使用多线程技术在编程上要解决线程安全问题，主要可以考虑以下几个方面：<ul><li>将对象设计为无状态对象（这和面向对象的编程观点是矛盾的，在面向对象的世界中被视为不良设计），这样就不会存在并发访问时对象状态不一致的问题。</li><li>在方法内部创建对象，这样对象由进入方法的线程创建，不会出现多个线程访问同一对象的问题。使用 ThreadLocal 将对象与线程绑定也是很好的做法，这一点在前面已经探讨过了。</li><li>对资源进行并发访问时应当使用合理的锁机制。</li></ul></li><li>非阻塞 I/O： 使用单线程和非阻塞 I/O 是目前公认的比多线程的方式更能充分发挥服务器性能的应用模式，基于 Node.js 构建的服务器就采用了这样的方式。Java 在 JDK 1.4 中就引入了 NIO（Non-blocking I/O）,在 Servlet 3 规范中又引入了异步 Servlet 的概念，这些都为在服务器端采用非阻塞 I/O 提供了必要的基础。</li><li>资源复用：资源复用主要有两种方式，一是单例，二是对象池，我们使用的数据库连接池、线程池都是对象池化技术，这是典型的用空间换取时间的策略，另一方面也实现对资源的复用，从而避免了不必要的创建和释放资源所带来的开销。</li></ul></li></ul><h2 id="什么是-XSS-攻击？什么是-SQL-注入攻击？什么是-CSRF-攻击？"><a href="#什么是-XSS-攻击？什么是-SQL-注入攻击？什么是-CSRF-攻击？" class="headerlink" title="什么是 XSS 攻击？什么是 SQL 注入攻击？什么是 CSRF 攻击？"></a>什么是 XSS 攻击？什么是 SQL 注入攻击？什么是 CSRF 攻击？</h2><p>答：</p><ul><li><p>XSS（Cross Site Script，跨站脚本攻击）是向网页中注入恶意脚本在用户浏览网页时在用户浏览器中执行恶意脚本的攻击方式。跨站脚本攻击分有两种形式：反射型攻击（诱使用户点击一个嵌入恶意脚本的链接以达到攻击的目标，目前有很多攻击者利用论坛、微博发布含有恶意脚本的 URL 就属于这种方式）和持久型攻击（将恶意脚本提交到被攻击网站的数据库中，用户浏览网页时，恶意脚本从数据库中被加载到页面执行，QQ 邮箱的早期版本就曾经被利用作为持久型跨站脚本攻击的平台）。XSS 虽然不是什么新鲜玩意，但是攻击的手法却不断翻新，防范 XSS 主要有两方面：消毒（对危险字符进行转义）和 HttpOnly（防范 XSS 攻击者窃取 Cookie 数据）。</p></li><li><p>SQL 注入攻击是注入攻击最常见的形式（此外还有 OS 注入攻击（Struts 2 的高危漏洞就是通过 OGNL 实施 OS 注入攻击导致的）），当服务器使用请求参数构造 SQL 语句时，恶意的 SQL 被嵌入到 SQL 中交给数据库执行。SQL 注入攻击需要攻击者对数据库结构有所了解才能进行，攻击者想要获得表结构有多种方式：(1)如果使用开源系统搭建网站，数据库结构也是公开的（目前有很多现成的系统可以直接搭建论坛，电商网站，虽然方便快捷但是风险是必须要认真评估的）；(2)错误回显（如果将服务器的错误信息直接显示在页面上，攻击者可以通过非法参数引发页面错误从而通过错误信息了解数据库结构，Web 应用应当设置友好的错误页，一方面符合最小惊讶原则，一方面屏蔽掉可能给系统带来危险的错误回显信息）；(3)盲注。防范 SQL 注入攻击也可以采用消毒的方式，通过正则表达式对请求参数进行验证，此外，参数绑定也是很好的手段，这样恶意的 SQL 会被当做 SQL 的参数而不是命令被执行，JDBC 中的 <code>PreparedStatement</code> 就是支持参数绑定的语句对象，从性能和安全性上都明显优于 <code>Statement</code>。</p></li><li><p>CSRF 攻击（Cross Site Request Forgery，跨站请求伪造）是攻击者通过跨站请求，以合法的用户身份进行非法操作（如转账或发帖等）。CSRF 的原理是利用浏览器的 Cookie 或服务器的 Session，盗取用户身份，其原理如下图所示。防范 CSRF 的主要手段是识别请求者的身份，主要有以下几种方式：(1)在表单中添加令牌（token）；(2)验证码；(3)检查请求头中的 Referer（前面提到防图片盗链接也是用的这种方式）。令牌和验证都具有一次消费性的特征，因此在原理上一致的，但是验证码是一种糟糕的用户体验，不是必要的情况下不要轻易使用验证码，目前很多网站的做法是如果在短时间内多次提交一个表单未获得成功后才要求提供验证码，这样会获得较好的用户体验。</p></li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/168.png" alt="CSRF 攻击"></p><p><strong>补充：</strong>防火墙的架设是 Web 安全的重要保障，<a href="https://www.modsecurity.org/">ModSecurity</a> 是开源的 Web 防火墙中的佼佼者。企业级防火墙的架设应当有两级防火墙，Web 服务器和部分应用服务器可以架设在两级防火墙之间的 DMZ，而数据和资源服务器应当架设在第二级防火墙之后。</p><h2 id="什么是领域模型（domain-model）？贫血模型（anaemic-domain-model）和充血模型（rich-domain-model）有什么区别？"><a href="#什么是领域模型（domain-model）？贫血模型（anaemic-domain-model）和充血模型（rich-domain-model）有什么区别？" class="headerlink" title="什么是领域模型（domain model）？贫血模型（anaemic domain model）和充血模型（rich domain model）有什么区别？"></a>什么是领域模型（domain model）？贫血模型（anaemic domain model）和充血模型（rich domain model）有什么区别？</h2><p>答：领域模型是领域内的概念类或现实世界中对象的可视化表示，又称为概念模型或分析对象模型，它专注于分析问题领域本身，发掘重要的业务领域概念，并建立业务领域概念之间的关系。贫血模型是指使用的领域对象中只有 <code>setter</code> 和 <code>getter</code> 方法（POJO），所有的业务逻辑都不包含在领域对象中而是放在业务逻辑层。有人将我们这里说的贫血模型进一步划分成失血模型（领域对象完全没有业务逻辑）和贫血模型（领域对象有少量的业务逻辑），我们这里就不对此加以区分了。充血模型将大多数业务逻辑和持久化放在领域对象中，业务逻辑（业务门面）只是完成对业务逻辑的封装、事务和权限等的处理。下面两张图分别展示了贫血模型和充血模型的分层架构。</p><p>贫血模型</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/169_1.png" alt="贫血模型"></p><p>充血模型</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/169_2.png" alt="充血模型"></p><p>贫血模型下组织领域逻辑通常使用事务脚本模式，让每个过程对应用户可能要做的一个动作，每个动作由一个过程来驱动。也就是说在设计业务逻辑接口的时候，每个方法对应着用户的一个操作，这种模式有以下几个有点：</p><ul><li>它是一个大多数开发者都能够理解的简单过程模型（适合国内的绝大多数开发者）。</li><li>它能够与一个使用行数据入口或表数据入口的简单数据访问层很好的协作。</li><li>事务边界的显而易见，一个事务开始于脚本的开始，终止于脚本的结束，很容易通过代理（或切面）实现声明式事务。</li></ul><p>然而，事务脚本模式的缺点也是很多的，随着领域逻辑复杂性的增加，系统的复杂性将迅速增加，程序结构将变得极度混乱。开源中国社区上有一篇很好的译文<a href="https://www.oschina.net/translate/how-anaemic-domain-models-cause-bad-software">《贫血领域模型是如何导致糟糕的软件产生》</a>对这个问题做了比较细致的阐述。</p><h2 id="谈一谈测试驱动开发（TDD）的好处以及你的理解。"><a href="#谈一谈测试驱动开发（TDD）的好处以及你的理解。" class="headerlink" title="谈一谈测试驱动开发（TDD）的好处以及你的理解。"></a>谈一谈测试驱动开发（TDD）的好处以及你的理解。</h2><p>答：TDD 是指在编写真正的功能实现代码之前先写测试代码，然后根据需要重构实现代码。在 JUnit 的作者 Kent Beck 的大作《测试驱动开发：实战与模式解析》（Test-Driven Development: by Example）一书中有这么一段内容：“消除恐惧和不确定性是编写测试驱动代码的重要原因”。因为编写代码时的恐惧会让你小心试探，让你回避沟通，让你羞于得到反馈，让你变得焦躁不安，而 TDD 是消除恐惧、让 Java 开发者更加自信更加乐于沟通的重要手段。TDD 会带来的好处可能不会马上呈现，但是你在某个时候一定会发现，这些好处包括：</p><ul><li>更清晰的代码 — 只写需要的代码</li><li>更好的设计</li><li>更出色的灵活性 — 鼓励程序员面向接口编程</li><li>更快速的反馈 — 不会到系统上线时才知道 bug 的存在</li></ul><p><strong>补充：</strong>敏捷软件开发的概念已经有很多年了，而且也部分的改变了软件开发这个行业，TDD 也是敏捷开发所倡导的。</p><p>TDD 可以在多个层级上应用，包括单元测试（测试一个类中的代码）、集成测试（测试类之间的交互）、系统测试（测试运行的系统）和系统集成测试（测试运行的系统包括使用的第三方组件）。TDD 的实施步骤是：红（失败测试）- 绿（通过测试） - 重构。关于实施 TDD 的详细步骤请参考另一篇文章<a href="https://blog.csdn.net/jackfrued/article/details/44433249">《测试驱动开发之初窥门径》</a>。</p><p>在使用 TDD 开发时，经常会遇到需要被测对象需要依赖其他子系统的情况，但是你希望将测试代码跟依赖项隔离，以保证测试代码仅仅针对当前被测对象或方法展开，这时候你需要的是测试替身。测试替身可以分为四类：</p><ul><li>虚设替身：只传递但是不会使用到的对象，一般用于填充方法的参数列表</li><li>存根替身：总是返回相同的预设响应，其中可能包括一些虚设状态</li><li>伪装替身：可以取代真实版本的可用版本（比真实版本还是会差很多）</li><li>模拟替身：可以表示一系列期望值的对象，并且可以提供预设响应</li></ul><p>Java 世界中实现模拟替身的第三方工具非常多，包括 EasyMock、Mockito、jMock 等。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Java/">Java</category>
      
      
      <comments>https://blog.eurkon.com/post/dc04fa32.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Java 面试题解析（Java Web）</title>
      <link>https://blog.eurkon.com/post/d24a315e.html</link>
      <guid>https://blog.eurkon.com/post/d24a315e.html</guid>
      <pubDate>Mon, 22 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;阐述-Servlet-和-CGI-的区别&quot;&gt;&lt;a href=&quot;#阐述-Servlet-和-CGI-的区别&quot; class=&quot;headerlink&quot; title=&quot;阐述 Servlet 和 CGI 的区别?&quot;&gt;&lt;/a&gt;阐述 Servlet 和 CGI 的区别?&lt;/h2</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="阐述-Servlet-和-CGI-的区别"><a href="#阐述-Servlet-和-CGI-的区别" class="headerlink" title="阐述 Servlet 和 CGI 的区别?"></a>阐述 Servlet 和 CGI 的区别?</h2><p>答：Servlet 与 CGI 的区别在于 Servlet 处于服务器进程中，它通过多线程方式运行其 <code>service()</code> 方法，一个实例可以服务于多个请求，并且其实例一般不会销毁，而 CGI 对每个请求都产生新的进程，服务完成后就销毁，所以效率上低于 Servlet。</p><p><strong>补充：</strong>Sun Microsystems 公司在 1996 年发布 Servlet 技术就是为了和 CGI 进行竞争，Servlet 是一个特殊的 Java 程序，一个基于 Java 的 Web 应用通常包含一个或多个 Servlet 类。Servlet 不能够自行创建并执行，它是在 Servlet 容器中运行的，容器将用户的请求传递给 Servlet 程序，并将 Servlet 的响应回传给用户。通常一个 Servlet 会关联一个或多个 JSP 页面。以前 CGI 经常因为性能开销上的问题被诟病，然而 Fast CGI 早就已经解决了 CGI 效率上的问题，所以面试的时候大可不必信口开河的诟病 CGI，事实上有很多你熟悉的网站都使用了 CGI 技术。</p><h2 id="Servlet-接口中有哪些方法？"><a href="#Servlet-接口中有哪些方法？" class="headerlink" title="Servlet 接口中有哪些方法？"></a>Servlet 接口中有哪些方法？</h2><p>答：Servlet 接口定义了 5 个方法，其中前三个方法与 Servlet 生命周期相关：</p><ul><li><code>void init(ServletConfig config) throws ServletException</code></li><li><code>void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException</code></li><li><code>void destroy()</code></li><li><code>java.lang.String getServletInfo()</code></li><li><code>ServletConfig getServletConfig()</code></li></ul><p>Web 容器加载 Servlet 并将其实例化后，Servlet 生命周期开始，容器运行其 <code>init()</code> 方法进行 Servlet 的初始化；请求到达时调用 Servlet 的 <code>service()</code> 方法，<code>service()</code> 方法会根据需要调用与请求对应的 <code>doGet</code> 或 <code>doPost</code> 等方法；当服务器关闭或项目被卸载时服务器会将 Servlet 实例销毁，此时会调用 Servlet 的 <code>destroy()</code> 方法。</p><h2 id="转发（forward）和重定向（redirect）的区别？"><a href="#转发（forward）和重定向（redirect）的区别？" class="headerlink" title="转发（forward）和重定向（redirect）的区别？"></a>转发（forward）和重定向（redirect）的区别？</h2><p>答：<code>forward</code> 是容器中控制权的转向，是服务器请求资源，服务器直接访问目标地址的 URL，把那个 URL 的响应内容读取过来，然后把这些内容再发给浏览器，浏览器根本不知道服务器发送的内容是从哪儿来的，所以它的地址栏中还是原来的地址。<code>redirect</code> 就是服务器端根据逻辑，发送一个状态码，告诉浏览器重新去请求那个地址，因此从浏览器的地址栏中可以看到跳转后的链接地址，很明显 <code>redirect</code> 无法访问到服务器保护起来资源，但是可以从一个网站 <code>redirect</code> 到其他网站。<code>forward</code> 更加高效，所以在满足需要时尽量使用 <code>forward</code>（通过调用 <code>RequestDispatcher</code> 对象的 <code>forward()</code> 方法，该对象可以通过 <code>ServletRequest</code> 对象的 <code>getRequestDispatcher()</code> 方法获得），并且这样也有助于隐藏实际的链接；在有些情况下，比如需要访问一个其它服务器上的资源，则必须使用重定向（通过 <code>HttpServletResponse</code> 对象调用其 <code>sendRedirect()</code> 方法实现）。</p><h2 id="JSP-有哪些内置对象？作用分别是什么？"><a href="#JSP-有哪些内置对象？作用分别是什么？" class="headerlink" title="JSP 有哪些内置对象？作用分别是什么？"></a>JSP 有哪些内置对象？作用分别是什么？</h2><p>答：JSP 有 9 个内置对象：</p><ul><li><code>request</code>：封装客户端的请求，其中包含来自 GET 或 POST 请求的参数；</li><li><code>response</code>：封装服务器对客户端的响应；</li><li><code>pageContext</code>：通过该对象可以获取其他对象；</li><li><code>session</code>：封装用户会话的对象；</li><li><code>application</code>：封装服务器运行环境的对象；</li><li><code>out</code>：输出服务器响应的输出流对象；</li><li><code>config</code>：Web 应用的配置对象；</li><li><code>page</code>：JSP 页面本身（相当于 Java 程序中的 <code>this</code>）；</li><li><code>exception</code>：封装页面抛出异常的对象。</li></ul><p><strong>补充：</strong>如果用 Servlet 来生成网页中的动态内容无疑是非常繁琐的工作，另一方面，所有的文本和 HTML 标签都是硬编码，即使做出微小的修改，都需要进行重新编译。JSP 解决了 Servlet 的这些问题，它是 Servlet 很好的补充，可以专门用作为用户呈现视图（View），而 Servlet 作为控制器（Controller）专门负责处理用户请求并转发或重定向到某个页面。基于 Java 的 Web 开发很多都同时使用了 Servlet 和 JSP。JSP 页面其实是一个 Servlet，能够运行 Servlet 的服务器（Servlet 容器）通常也是 JSP 容器，可以提供 JSP 页面的运行环境，Tomcat 就是一个 Servlet/JSP 容器。第一次请求一个 JSP 页面时，Servlet/JSP 容器首先将 JSP 页面转换成一个 JSP 页面的实现类，这是一个实现了 JspPage 接口或其子接口 HttpJspPage 的 Java 类。JspPage 接口是 Servlet 的子接口，因此每个 JSP 页面都是一个 Servlet。转换成功后，容器会编译 Servlet 类，之后容器加载和实例化 Java 字节码，并执行它通常对 Servlet 所做的生命周期操作。对同一个 JSP 页面的后续请求，容器会查看这个 JSP 页面是否被修改过，如果修改过就会重新转换并重新编译并执行。如果没有则执行内存中已经存在的 Servlet 实例。我们可以看一段 JSP 代码对应的 Java 程序就知道一切了，而且 9 个内置对象的神秘面纱也会被揭开。</p><p>JSP 页面：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">&lt;%@ page pageEncoding=<span class="string">&quot;UTF-8&quot;</span>%&gt;</span><br><span class="line">&lt;%</span><br><span class="line">String path = request.getContextPath();</span><br><span class="line">String basePath = request.getScheme() + <span class="string">&quot;://&quot;</span> + request.getServerName() + <span class="string">&quot;:&quot;</span> + request.getServerPort() + path + <span class="string">&quot;/&quot;</span>;</span><br><span class="line">%&gt;</span><br><span class="line"></span><br><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">  &lt;head&gt;</span><br><span class="line">    &lt;base href=<span class="string">&quot;&lt;%=basePath%&gt;&quot;</span>&gt;</span><br><span class="line">    &lt;title&gt;首页&lt;/title&gt;</span><br><span class="line">    &lt;style type=<span class="string">&quot;text/css&quot;</span>&gt;</span><br><span class="line">        * &#123; font-family: <span class="string">&quot;Arial&quot;</span>; &#125;</span><br><span class="line">    &lt;/style&gt;</span><br><span class="line">  &lt;/head&gt;</span><br><span class="line"></span><br><span class="line">  &lt;body&gt;</span><br><span class="line">    &lt;h1&gt;Hello, World!&lt;/h1&gt;</span><br><span class="line">    &lt;hr/&gt;</span><br><span class="line">    &lt;h2&gt;Current time is: &lt;%= new java.util.Date().toString() %&gt;&lt;/h2&gt;</span><br><span class="line">  &lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure></p><p>对应的 Java 代码：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Generated by the Jasper component of Apache Tomcat</span></span><br><span class="line"><span class="comment"> * Version: Apache Tomcat/7.0.52</span></span><br><span class="line"><span class="comment"> * Generated at: 2014-10-13 13:28:38 UTC</span></span><br><span class="line"><span class="comment"> * Note: The last modified time of this file was set to</span></span><br><span class="line"><span class="comment"> *       the last modified time of the source file after</span></span><br><span class="line"><span class="comment"> *       generation to assist with modification tracking.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">package</span> org.apache.jsp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.*;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.*;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.jsp.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">index_jsp</span> <span class="keyword">extends</span> <span class="title">org</span>.<span class="title">apache</span>.<span class="title">jasper</span>.<span class="title">runtime</span>.<span class="title">HttpJspBase</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">org</span>.<span class="title">apache</span>.<span class="title">jasper</span>.<span class="title">runtime</span>.<span class="title">JspSourceDependent</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> javax.servlet.jsp.JspFactory _jspxFactory = javax.servlet.jsp.JspFactory</span><br><span class="line">            .getDefaultFactory();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> java.util.Map&lt;java.lang.String, java.lang.Long&gt; _jspx_dependants;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> javax.el.ExpressionFactory _el_expressionfactory;</span><br><span class="line">    <span class="keyword">private</span> org.apache.tomcat.InstanceManager _jsp_instancemanager;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> java.util.Map&lt;java.lang.String, java.lang.Long&gt; getDependants() &#123;</span><br><span class="line">        <span class="keyword">return</span> _jspx_dependants;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">_jspInit</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        _el_expressionfactory = _jspxFactory.getJspApplicationContext(</span><br><span class="line">                getServletConfig().getServletContext()).getExpressionFactory();</span><br><span class="line">        _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory</span><br><span class="line">                .getInstanceManager(getServletConfig());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">_jspDestroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">_jspService</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">final</span> javax.servlet.http.HttpServletRequest request,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">final</span> javax.servlet.http.HttpServletResponse response)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> java.io.IOException, javax.servlet.ServletException </span>&#123;</span><br><span class="line">        <span class="comment">// 内置对象就是在这里定义的</span></span><br><span class="line">        <span class="keyword">final</span> javax.servlet.jsp.PageContext pageContext;</span><br><span class="line">        javax.servlet.http.HttpSession session = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">final</span> javax.servlet.ServletContext application;</span><br><span class="line">        <span class="keyword">final</span> javax.servlet.ServletConfig config;</span><br><span class="line">        javax.servlet.jsp.JspWriter out = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">final</span> java.lang.Object page = <span class="keyword">this</span>;</span><br><span class="line">        javax.servlet.jsp.JspWriter _jspx_out = <span class="keyword">null</span>;</span><br><span class="line">        javax.servlet.jsp.PageContext _jspx_page_context = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            response.setContentType(<span class="string">&quot;text/html;charset=UTF-8&quot;</span>);</span><br><span class="line">            pageContext = _jspxFactory.getPageContext(<span class="keyword">this</span>, request, response,</span><br><span class="line">                    <span class="keyword">null</span>, <span class="keyword">true</span>, <span class="number">8192</span>, <span class="keyword">true</span>);</span><br><span class="line">            _jspx_page_context = pageContext;</span><br><span class="line">            application = pageContext.getServletContext();</span><br><span class="line">            config = pageContext.getServletConfig();</span><br><span class="line">            session = pageContext.getSession();</span><br><span class="line">            out = pageContext.getOut();</span><br><span class="line">            _jspx_out = out;</span><br><span class="line"></span><br><span class="line">            out.write(<span class="string">&#x27;\r&#x27;</span>);</span><br><span class="line">            out.write(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line"></span><br><span class="line">            String path = request.getContextPath();</span><br><span class="line">            String basePath = request.getScheme() + <span class="string">&quot;://&quot;</span></span><br><span class="line">                    + request.getServerName() + <span class="string">&quot;:&quot;</span> + request.getServerPort()</span><br><span class="line">                    + path + <span class="string">&quot;/&quot;</span>;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 以下代码通过输出流将 HTML 标签输出到浏览器中</span></span><br><span class="line">            out.write(<span class="string">&quot;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;&lt;!DOCTYPE html&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;&lt;html&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;  &lt;head&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;    &lt;base href=\&quot;&quot;</span>);</span><br><span class="line">            out.print(basePath);</span><br><span class="line">            out.write(<span class="string">&quot;\&quot;&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;    &lt;title&gt;首页&lt;/title&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;    &lt;style type=\&quot;text/css\&quot;&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;    \t* &#123; font-family: \&quot;Arial\&quot;; &#125;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;    &lt;/style&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;  &lt;/head&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;  \r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;  &lt;body&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;    &lt;h1&gt;Hello, World!&lt;/h1&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;    &lt;hr/&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;    &lt;h2&gt;Current time is: &quot;</span>);</span><br><span class="line">            out.print(<span class="keyword">new</span> java.util.Date().toString());</span><br><span class="line">            out.write(<span class="string">&quot;&lt;/h2&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;  &lt;/body&gt;\r\n&quot;</span>);</span><br><span class="line">            out.write(<span class="string">&quot;&lt;/html&gt;\r\n&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (java.lang.Throwable t) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!(t <span class="keyword">instanceof</span> javax.servlet.jsp.SkipPageException)) &#123;</span><br><span class="line">                out = _jspx_out;</span><br><span class="line">                <span class="keyword">if</span> (out != <span class="keyword">null</span> &amp;&amp; out.getBufferSize() != <span class="number">0</span>)</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        out.clearBuffer();</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (java.io.IOException e) &#123;</span><br><span class="line">                    &#125;</span><br><span class="line">                <span class="keyword">if</span> (_jspx_page_context != <span class="keyword">null</span>)</span><br><span class="line">                    _jspx_page_context.handlePageException(t);</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> ServletException(t);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            _jspxFactory.releasePageContext(_jspx_page_context);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="get-和-post-请求的区别？"><a href="#get-和-post-请求的区别？" class="headerlink" title="get 和 post 请求的区别？"></a>get 和 post 请求的区别？</h2><p>答：</p><ol><li>get 请求用来从服务器上获得资源，而 post 是用来向服务器提交数据；</li><li>get 将表单中数据按照 <code>name=value</code> 的形式，添加到 <code>action</code> 所指向的 URL 后面，并且两者使用 <code>?</code> 连接，而各个变量之间使用 <code>&amp;</code> 连接；post 是将表单中的数据放在 HTTP 协议的请求头或消息体中，传递到 <code>action</code> 所指向 URL；</li><li>get 传输的数据要受到 URL 长度限制（1024 字节）；而 post 可以传输大量的数据，上传文件通常要使用 post 方式；</li><li>使用 get 时参数会显示在地址栏上，如果这些数据不是敏感数据，那么可以使用 get；对于敏感数据还是应用使用 post；</li><li>get 使用 MIME 类型 <code>application/x-www-form-urlencoded</code> 的 URL 编码（也叫百分号编码）文本的格式传递参数，保证被传送的参数由遵循规范的文本组成，例如一个空格的编码是 <code>%20</code>。</li></ol><h2 id="常用的-Web-服务器有哪些？"><a href="#常用的-Web-服务器有哪些？" class="headerlink" title="常用的 Web 服务器有哪些？"></a>常用的 Web 服务器有哪些？</h2><p>答：Unix 和 Linux 平台下使用最广泛的免费 HTTP 服务器是 Apache 服务器，而 Windows 平台的服务器通常使用 IIS 作为 Web 服务器。选择 Web 服务器应考虑的因素有：性能、安全性、日志和统计、虚拟主机、代理服务器、缓冲服务和集成应用程序等。下面是对常见服务器的简介：</p><ul><li><p>IIS：Microsoft 的 Web 服务器产品，全称是 Internet Information Services。IIS 是允许在公共 Intranet 或 Internet 上发布信息的 Web 服务器。IIS 是目前最流行的 Web 服务器产品之一，很多著名的网站都是建立在 IIS 的平台上。IIS 提供了一个图形界面的管理工具，称为 Internet 服务管理器，可用于监视配置和控制 Internet 服务。IIS 是一种 Web 服务组件，其中包括 Web 服务器、FTP 服务器、NNTP 服务器和 SMTP 服务器，分别用于网页浏览、文件传输、新闻服务和邮件发送等方面，它使得在网络（包括互联网和局域网）上发布信息成了一件很容易的事。它提供 ISAPI（Intranet Server API）作为扩展 Web 服务器功能的编程接口；同时，它还提供一个 Internet 数据库连接器，可以实现对数据库的查询和更新。</p></li><li><p>Kangle：Kangle Web 服务器是一款跨平台、功能强大、安全稳定、易操作的高性能 Web 服务器和反向代理服务器软件。此外，Kangle 也是一款专为做虚拟主机研发的 Web 服务器。实现虚拟主机独立进程、独立身份运行。用户之间安全隔离，一个用户出问题不影响其他用户。支持 PHP、ASP、ASP.NET、Java、Ruby 等多种动态开发语言。</p></li><li><p>WebSphere：WebSphere Application Server 是功能完善、开放的 Web 应用程序服务器，是 IBM 电子商务计划的核心部分，它是基于 Java 的应用环境，用于建立、部署和管理 Internet 和 Intranet Web 应用程序，适应各种 Web 应用程序服务器的需要。</p></li><li><p>WebLogic：WebLogic Server 是一款多功能、基于标准的 Web 应用服务器，为企业构建企业应用提供了坚实的基础。针对各种应用开发、关键性任务的部署，各种系统和数据库的集成、跨 Internet 协作等 Weblogic 都提供了相应的支持。由于它具有全面的功能、对开放标准的遵从性、多层架构、支持基于组件的开发等优势，很多公司的企业级应用都选择它来作为开发和部署的环境。WebLogic Server 在使应用服务器成为企业应用架构的基础方面一直处于领先地位，为构建集成化的企业级应用提供了稳固的基础。</p></li><li><p>Apache：目前 Apache 仍然是世界上用得最多的 Web 服务器，其市场占有率很长时间都保持在 60% 以上（目前的市场份额约 40% 左右）。世界上很多著名的网站都是 Apache 的产物，它的成功之处主要在于它的源代码开放、有一支强大的开发团队、支持跨平台的应用（可以运行在几乎所有的 Unix、Windows、Linux 系统平台上）以及它的可移植性等方面。</p></li><li><p>Tomcat：Tomcat 是一个开放源代码、运行 Servlet 和 JSP 的容器。Tomcat 实现了 Servlet 和 JSP 规范。此外，Tomcat 还实现了 Apache-Jakarta 规范而且比绝大多数商业应用软件服务器要好，因此目前也有不少的 Web 服务器都选择了 Tomcat。</p></li><li><p>Nginx：读作&quot;engine x&quot;，是一个高性能的 HTTP 和反向代理服务器，也是一个 IMAP/POP3/SMTP 代理服务器。 Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler 站点开发的，第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。其将源代码以类 BSD 许可证的形式发布，因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。在 2014 年下半年，Nginx 的市场份额达到了 14%。</p></li></ul><h2 id="JSP-和-Servlet-是什么关系？"><a href="#JSP-和-Servlet-是什么关系？" class="headerlink" title="JSP 和 Servlet 是什么关系？"></a>JSP 和 Servlet 是什么关系？</h2><p>答：其实这个问题在上面已经阐述过了，Servlet 是一个特殊的 Java 程序，它运行于服务器的 JVM 中，能够依靠服务器的支持向浏览器提供显示内容。JSP 本质上是 Servlet 的一种简易形式，JSP 会被服务器处理成一个类似于 Servlet 的 Java 程序，可以简化页面内容的生成。Servlet 和 JSP 最主要的不同点在于，Servlet 的应用逻辑是在 Java 文件中，并且完全从表示层中的 HTML 分离开来。而 JSP 的情况是 Java 和 HTML 可以组合成一个扩展名为 <code>.jsp</code> 的文件。有人说，Servlet 就是在 Java 中写 HTML，而 JSP 就是在 HTML 中写 Java 代码，当然这个说法是很片面且不够准确的。JSP 侧重于视图，Servlet 更侧重于控制逻辑，在 MVC 架构模式中，JSP 适合充当视图（view）而 Servlet 适合充当控制器（controller）。</p><h2 id="讲解-JSP-中的四种作用域。"><a href="#讲解-JSP-中的四种作用域。" class="headerlink" title="讲解 JSP 中的四种作用域。"></a>讲解 JSP 中的四种作用域。</h2><p>答：JSP 中的四种作用域包括 <code>page</code>、<code>request</code>、<code>session</code> 和 <code>application</code>，具体来说：</p><ul><li><code>page</code> 代表与一个页面相关的对象和属性。</li><li><code>request</code> 代表与 Web 客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面，涉及多个 Web 组件；需要在页面显示的临时数据可以置于此作用域。</li><li><code>session</code> 代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的 session 中。</li><li><code>application</code> 代表与整个 Web 应用程序相关的对象和属性，它实质上是跨越整个 Web 应用程序，包括多个页面、请求和会话的一个全局作用域。</li></ul><h2 id="如何实现-JSP-或-Servlet-的单线程模式？"><a href="#如何实现-JSP-或-Servlet-的单线程模式？" class="headerlink" title="如何实现 JSP 或 Servlet 的单线程模式？"></a>如何实现 JSP 或 Servlet 的单线程模式？</h2><p>答：</p><p>对于 JSP 页面，可以通过 <code>page</code> 指令进行设置。</p><p><figure class="highlight jsp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;%<span class="meta">@page</span> isThreadSafe=”<span class="keyword">false</span>”%&gt;</span><br></pre></td></tr></table></figure></p><p>对于 Servlet，可以让自定义的 Servlet 实现 <code>SingleThreadModel</code> 标识接口。</p><p><strong>说明：</strong>如果将 JSP 或 Servlet 设置成单线程工作模式，会导致每个请求创建一个 Servlet 实例，这种实践将导致严重的性能问题（服务器的内存压力很大，还会导致频繁的垃圾回收），所以通常情况下并不会这么做。</p><h2 id="实现会话跟踪的技术有哪些？"><a href="#实现会话跟踪的技术有哪些？" class="headerlink" title="实现会话跟踪的技术有哪些？"></a>实现会话跟踪的技术有哪些？</h2><p>答：由于 HTTP 协议本身是无状态的，服务器为了区分不同的用户，就需要对用户会话进行跟踪，简单的说就是为用户进行登记，为用户分配唯一的 ID，下一次用户在请求中包含此 ID，服务器据此判断到底是哪一个用户。</p><ol><li>URL 重写：在 URL 中添加用户会话的信息作为请求的参数，或者将唯一的会话 ID 添加到 URL 结尾以标识一个会话。</li><li>设置表单隐藏域：将和会话跟踪相关的字段添加到隐式表单域中，这些信息不会在浏览器中显示但是提交表单时会提交给服务器。这两种方式很难处理跨越多个页面的信息传递，因为如果每次都要修改 URL 或在页面中添加隐式表单域来存储用户会话相关信息，事情将变得非常麻烦。</li><li>cookie：cookie 有两种，一种是基于窗口的，浏览器窗口关闭后，cookie 就没有了；另一种是将信息存储在一个临时文件中，并设置存在的时间。当用户通过浏览器和服务器建立一次会话后，会话 ID 就会随响应信息返回存储在基于窗口的 cookie 中，那就意味着只要浏览器没有关闭，会话没有超时，下一次请求时这个会话 ID 又会提交给服务器让服务器识别用户身份。会话中可以为用户保存信息。会话对象是在服务器内存中的，而基于窗口的 cookie 是在客户端内存中的。如果浏览器禁用了 cookie，那么就需要通过下面两种方式进行会话跟踪。当然，在使用 cookie 时要注意几点：首先不要在 cookie 中存放敏感信息；其次 cookie 存储的数据量有限（4k），不能将过多的内容存储 cookie 中；再者浏览器通常只允许一个站点最多存放 20 个 cookie。当然，和用户会话相关的其他信息（除了会话 ID）也可以存在 cookie 方便进行会话跟踪。</li><li>HttpSession：在所有会话跟踪技术中，HttpSession 对象是最强大也是功能最多的。当一个用户第一次访问某个网站时会自动创建 HttpSession，每个用户可以访问他自己的 HttpSession。可以通过 <code>HttpServletRequest</code> 对象的 <code>getSession</code> 方法获得 HttpSession，通过 HttpSession 的 <code>setAttribute</code> 方法可以将一个值放在 HttpSession 中，通过调用 HttpSession 对象的 <code>getAttribute</code> 方法，同时传入属性名就可以获取保存在 HttpSession 中的对象。与上面三种方式不同的是，HttpSession 放在服务器的内存中，因此不要将过大的对象放在里面，即使目前的 Servlet 容器可以在内存将满时将 HttpSession 中的对象移到其他存储设备中，但是这样势必影响性能。添加到 HttpSession 中的值可以是任意 Java 对象，这个对象最好实现了 <code>Serializable</code> 接口，这样 Servlet 容器在必要的时候可以将其序列化到文件中，否则在序列化时就会出现异常。</li></ol><p><strong>补充：</strong>HTML5 中可以使用 Web Storage 技术通过 JavaScript 来保存数据，例如可以使用 <code>localStorage</code> 和 <code>sessionStorage</code> 来保存用户会话的信息，也能够实现会话跟踪。</p><h2 id="过滤器有哪些作用和用法？"><a href="#过滤器有哪些作用和用法？" class="headerlink" title="过滤器有哪些作用和用法？"></a>过滤器有哪些作用和用法？</h2><p>答：Java Web 开发中的过滤器（filter）是从 Servlet 2.3 规范开始增加的功能，并在 Servlet 2.4 规范中得到增强。对 Web 应用来说，过滤器是一个驻留在服务器端的 Web 组件，它可以截取客户端和服务器之间的请求与响应信息，并对这些信息进行过滤。当 Web 容器接受到一个对资源的请求时，它将判断是否有过滤器与这个资源相关联。如果有，那么容器将把请求交给过滤器进行处理。在过滤器中，你可以改变请求的内容，或者重新设置请求的报头信息，然后再将请求发送给目标资源。当目标资源对请求作出响应时候，容器同样会将响应先转发给过滤器，在过滤器中你可以对响应的内容进行转换，然后再将响应发送到客户端。</p><p>常见的过滤器用途主要包括：对用户请求进行统一认证、对用户的访问请求进行记录和审核、对用户发送的数据进行过滤或替换、转换图象格式、对响应内容进行压缩以减少传输量、对请求或响应进行加解密处理、触发资源访问事件、对 XML 的输出应用 XSLT 等。</p><p>和过滤器相关的接口主要有：<code>Filter</code>、<code>FilterConfig</code> 和 <code>FilterChain</code>。</p><p>编码过滤器的例子：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.Filter;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.FilterChain;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.FilterConfig;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletException;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletRequest;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletResponse;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.annotation.WebFilter;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.annotation.WebInitParam;</span><br><span class="line"></span><br><span class="line"><span class="meta">@WebFilter(urlPatterns = &#123; &quot;*&quot; &#125;, initParams = &#123;@WebInitParam(name=&quot;encoding&quot;, value=&quot;utf-8&quot;)&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CodingFilter</span> <span class="keyword">implements</span> <span class="title">Filter</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String defaultEncoding = <span class="string">&quot;utf-8&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">destroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doFilter</span><span class="params">(ServletRequest req, ServletResponse resp,</span></span></span><br><span class="line"><span class="function"><span class="params">            FilterChain chain)</span> <span class="keyword">throws</span> IOException, ServletException </span>&#123;</span><br><span class="line">        req.setCharacterEncoding(defaultEncoding);</span><br><span class="line">        resp.setCharacterEncoding(defaultEncoding);</span><br><span class="line">        chain.doFilter(req, resp);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(FilterConfig config)</span> <span class="keyword">throws</span> ServletException </span>&#123;</span><br><span class="line">        String encoding = config.getInitParameter(<span class="string">&quot;encoding&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (encoding != <span class="keyword">null</span>) &#123;</span><br><span class="line">            defaultEncoding = encoding;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>下载计数过滤器的例子：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.FileReader;</span><br><span class="line"><span class="keyword">import</span> java.io.FileWriter;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.util.Properties;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.ExecutorService;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Executors;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.Filter;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.FilterChain;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.FilterConfig;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletException;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletRequest;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletResponse;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.annotation.WebFilter;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletRequest;</span><br><span class="line"></span><br><span class="line"><span class="meta">@WebFilter(urlPatterns = &#123;&quot;/*&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DownloadCounterFilter</span> <span class="keyword">implements</span> <span class="title">Filter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> ExecutorService executorService = Executors.newSingleThreadExecutor();</span><br><span class="line">    <span class="keyword">private</span> Properties downloadLog;</span><br><span class="line">    <span class="keyword">private</span> File logFile;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">destroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        executorService.shutdown();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doFilter</span><span class="params">(ServletRequest req, ServletResponse resp,</span></span></span><br><span class="line"><span class="function"><span class="params">            FilterChain chain)</span> <span class="keyword">throws</span> IOException, ServletException </span>&#123;</span><br><span class="line">        HttpServletRequest request = (HttpServletRequest) req;</span><br><span class="line">        <span class="keyword">final</span> String uri = request.getRequestURI();</span><br><span class="line">        executorService.execute(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                String value = downloadLog.getProperty(uri);</span><br><span class="line">                <span class="keyword">if</span>(value == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    downloadLog.setProperty(uri, <span class="string">&quot;1&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">int</span> count = Integer.parseInt(value);</span><br><span class="line">                    downloadLog.setProperty(uri, String.valueOf(++count));</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    downloadLog.store(<span class="keyword">new</span> FileWriter(logFile), <span class="string">&quot;&quot;</span>);</span><br><span class="line">                &#125; </span><br><span class="line">                <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        chain.doFilter(req, resp);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(FilterConfig config)</span> <span class="keyword">throws</span> ServletException </span>&#123;</span><br><span class="line">        String appPath = config.getServletContext().getRealPath(<span class="string">&quot;/&quot;</span>);</span><br><span class="line">        logFile = <span class="keyword">new</span> File(appPath, <span class="string">&quot;downloadLog.txt&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span>(!logFile.exists()) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                logFile.createNewFile();</span><br><span class="line">            &#125; </span><br><span class="line">            <span class="keyword">catch</span>(IOException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        downloadLog = <span class="keyword">new</span> Properties();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            downloadLog.load(<span class="keyword">new</span> FileReader(logFile));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>说明：</strong>这里使用了 Servlet 3 规范中的注解来部署过滤器，当然也可以在 web.xml 中使用 <code>&lt;filter&gt;</code> 和 <code>&lt;filter-mapping&gt;</code> 标签部署过滤器，如 108 题中所示。</p><h2 id="监听器有哪些作用和用法？"><a href="#监听器有哪些作用和用法？" class="headerlink" title="监听器有哪些作用和用法？"></a>监听器有哪些作用和用法？</h2><p>答：Java Web 开发中的监听器（listener）就是 <code>application</code>、<code>session</code>、<code>request</code> 三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件，如下所示：</p><ol><li><code>ServletContextListener</code>：对 Servlet 上下文的创建和销毁进行监听。</li><li><code>ServletContextAttributeListener</code>：监听 Servlet 上下文属性的添加、删除和替换。</li><li><code>HttpSessionListener</code>：对 Session 的创建和销毁进行监听。</li><li><code>HttpSessionAttributeListener</code>：对 Session 对象中属性的添加、删除和替换进行监听。</li><li><code>ServletRequestListener</code>：对请求对象的初始化和销毁进行监听。</li><li><code>ServletRequestAttributeListener</code>：对请求对象属性的添加、删除和替换进行监听。</li></ol><p><strong>补充：</strong>session 的销毁有两种情况：(1)session 超时（可以在 web.xml 中通过 <code>&lt;session-config&gt;/&lt;session-timeout&gt;</code> 标签配置超时时间）；(2)通过调用 session 对象的 <code>invalidate()</code> 方法使 session 失效。</p><p>下面是一个统计网站最多在线人数监听器的例子。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.servlet.ServletContextEvent;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletContextListener;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.annotation.WebListener;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> 上下文监听器，在服务器启动时初始化 onLineCount 和 maxOnLineCount 两个变量</span></span><br><span class="line"><span class="comment"> 并将其置于服务器上下文（ServletContext）中，其初始值都是 0</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@WebListener</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">InitListener</span> <span class="keyword">implements</span> <span class="title">ServletContextListener</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">contextDestroyed</span><span class="params">(ServletContextEvent evt)</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">contextInitialized</span><span class="params">(ServletContextEvent evt)</span> </span>&#123;</span><br><span class="line">        evt.getServletContext().setAttribute(<span class="string">&quot;onLineCount&quot;</span>, <span class="number">0</span>);</span><br><span class="line">        evt.getServletContext().setAttribute(<span class="string">&quot;maxOnLineCount&quot;</span>, <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.text.DateFormat;</span><br><span class="line"><span class="keyword">import</span> java.text.SimpleDateFormat;</span><br><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletContext;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.annotation.WebListener;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpSessionEvent;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpSessionListener;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> 会话监听器，在用户会话创建和销毁的时候根据情况</span></span><br><span class="line"><span class="comment"> 修改 onLineCount 和 maxOnLineCount 的值</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@WebListener</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MaxCountListener</span> <span class="keyword">implements</span> <span class="title">HttpSessionListener</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionCreated</span><span class="params">(HttpSessionEvent event)</span> </span>&#123;</span><br><span class="line">        ServletContext ctx = event.getSession().getServletContext();</span><br><span class="line">        <span class="keyword">int</span> count = Integer.parseInt(ctx.getAttribute(<span class="string">&quot;onLineCount&quot;</span>).toString());</span><br><span class="line">        count++;</span><br><span class="line">        ctx.setAttribute(<span class="string">&quot;onLineCount&quot;</span>, count);</span><br><span class="line">        <span class="keyword">int</span> maxOnLineCount = Integer.parseInt(ctx.getAttribute(<span class="string">&quot;maxOnLineCount&quot;</span>).toString());</span><br><span class="line">        <span class="keyword">if</span> (count &gt; maxOnLineCount) &#123;</span><br><span class="line">            ctx.setAttribute(<span class="string">&quot;maxOnLineCount&quot;</span>, count);</span><br><span class="line">            DateFormat df = <span class="keyword">new</span> SimpleDateFormat(<span class="string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span>);</span><br><span class="line">            ctx.setAttribute(<span class="string">&quot;date&quot;</span>, df.format(<span class="keyword">new</span> Date()));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionDestroyed</span><span class="params">(HttpSessionEvent event)</span> </span>&#123;</span><br><span class="line">        ServletContext app = event.getSession().getServletContext();</span><br><span class="line">        <span class="keyword">int</span> count = Integer.parseInt(app.getAttribute(<span class="string">&quot;onLineCount&quot;</span>).toString());</span><br><span class="line">        count--;</span><br><span class="line">        app.setAttribute(<span class="string">&quot;onLineCount&quot;</span>, count);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>说明：</strong>这里使用了 Servlet 3 规范中的 @WebListener 注解配置监听器，当然你可以在 web.xml 文件中用 <code>&lt;listener&gt;</code> 标签配置监听器，如 108 题中所示。</p><h2 id="web-xml-文件中可以配置哪些内容？"><a href="#web-xml-文件中可以配置哪些内容？" class="headerlink" title="web.xml 文件中可以配置哪些内容？"></a>web.xml 文件中可以配置哪些内容？</h2><p>答：web.xml 用于配置 Web 应用的相关信息，如：监听器（listener）、过滤器（filter）、Servlet、相关参数、会话超时时间、安全验证方式、错误页面等，下面是一些开发中常见的配置：</p><ol><li>配置 Spring 上下文加载监听器加载 Spring 配置文件并创建 IoC 容器：</li></ol><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context-param</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">param-name</span>&gt;</span>contextConfigLocation<span class="tag">&lt;/<span class="name">param-name</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">param-value</span>&gt;</span>classpath:applicationContext.xml<span class="tag">&lt;/<span class="name">param-value</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">context-param</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">listener</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">listener-class</span>&gt;</span></span><br><span class="line">     org.springframework.web.context.ContextLoaderListener</span><br><span class="line">   <span class="tag">&lt;/<span class="name">listener-class</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">listener</span>&gt;</span></span><br></pre></td></tr></table></figure></p><ol><li>配置 Spring 的 OpenSessionInView 过滤器来解决延迟加载和 Hibernate 会话关闭的矛盾：</li></ol><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">filter</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">filter-name</span>&gt;</span>openSessionInView<span class="tag">&lt;/<span class="name">filter-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">filter-class</span>&gt;</span></span><br><span class="line">       org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</span><br><span class="line">    <span class="tag">&lt;/<span class="name">filter-class</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">filter</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">filter-mapping</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">filter-name</span>&gt;</span>openSessionInView<span class="tag">&lt;/<span class="name">filter-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>/*<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">filter-mapping</span>&gt;</span></span><br></pre></td></tr></table></figure></p><ol><li>配置会话超时时间为 10 分钟：</li></ol><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">session-config</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">session-timeout</span>&gt;</span>10<span class="tag">&lt;/<span class="name">session-timeout</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">session-config</span>&gt;</span></span><br></pre></td></tr></table></figure></p><ol><li>配置 404 和 Exception 的错误页面：</li></ol><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">error-page</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">error-code</span>&gt;</span>404<span class="tag">&lt;/<span class="name">error-code</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">location</span>&gt;</span>/error.jsp<span class="tag">&lt;/<span class="name">location</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">error-page</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">error-page</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">exception-type</span>&gt;</span>java.lang.Exception<span class="tag">&lt;/<span class="name">exception-type</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">location</span>&gt;</span>/error.jsp<span class="tag">&lt;/<span class="name">location</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">error-page</span>&gt;</span></span><br></pre></td></tr></table></figure></p><ol><li>配置安全认证方式：</li></ol><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">security-constraint</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">web-resource-collection</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">web-resource-name</span>&gt;</span>ProtectedArea<span class="tag">&lt;/<span class="name">web-resource-name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>/admin/*<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">http-method</span>&gt;</span>GET<span class="tag">&lt;/<span class="name">http-method</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">http-method</span>&gt;</span>POST<span class="tag">&lt;/<span class="name">http-method</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">web-resource-collection</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">auth-constraint</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">role-name</span>&gt;</span>admin<span class="tag">&lt;/<span class="name">role-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">auth-constraint</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">security-constraint</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">login-config</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">auth-method</span>&gt;</span>BASIC<span class="tag">&lt;/<span class="name">auth-method</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">login-config</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">security-role</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">role-name</span>&gt;</span>admin<span class="tag">&lt;/<span class="name">role-name</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">security-role</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p><strong>说明：</strong>对 Servlet（小服务）、Listener（监听器）和 Filter（过滤器）等 Web 组件的配置，Servlet 3 规范提供了基于注解的配置方式，可以分别使用 <code>@WebServlet</code>、<code>@WebListener</code>、<code>@WebFilter</code> 注解进行配置。</p><p><strong>补充：</strong>如果 Web 提供了有价值的商业信息或者是敏感数据，那么站点的安全性就是必须考虑的问题。安全认证是实现安全性的重要手段，认证就是要解决“Are you who you say you are?”的问题。认证的方式非常多，简单说来可以分为三类：</p><ul><li>A. What you know? — 口令</li><li>B. What you have? — 数字证书（U 盾、密保卡）</li><li>C. Who you are? — 指纹识别、虹膜识别</li></ul><p>在 Tomcat 中可以通过建立安全套接字层（Secure Socket Layer, SSL）以及通过基本验证或表单验证来实现对安全性的支持。</p><h2 id="你的项目中使用过哪些-JSTL-标签（JSP-标准标签库）？"><a href="#你的项目中使用过哪些-JSTL-标签（JSP-标准标签库）？" class="headerlink" title="你的项目中使用过哪些 JSTL 标签（JSP 标准标签库）？"></a>你的项目中使用过哪些 JSTL 标签（JSP 标准标签库）？</h2><p>答：项目中主要使用了 JSTL 的核心标签库，包括 <code>&lt;c:if&gt;</code>、<code>&lt;c:choose&gt;</code>、<code>&lt;c: when&gt;</code>、<code>&lt;c: otherwise&gt;</code>、<code>&lt;c:forEach&gt;</code> 等，主要用于构造循环和分支结构以控制显示逻辑。</p><p><strong>说明：</strong>虽然 JSTL 标签库提供了 <code>core</code>、<code>sql</code>、<code>fmt</code>、<code>xml</code> 等标签库，但是实际开发中建议只使用核心标签库（<code>core</code>），而且最好只使用分支和循环标签并辅以表达式语言（EL），这样才能真正做到数据显示和业务逻辑的分离，这才是最佳实践。</p><h2 id="使用标签库有什么好处？如何自定义-JSP-标签？"><a href="#使用标签库有什么好处？如何自定义-JSP-标签？" class="headerlink" title="使用标签库有什么好处？如何自定义 JSP 标签？"></a>使用标签库有什么好处？如何自定义 JSP 标签？</h2><p>答：使用标签库的好处包括以下几个方面：</p><ul><li>分离 JSP 页面的内容和逻辑，简化了 Web 开发；</li><li>开发者可以创建自定义标签来封装业务逻辑和显示逻辑；</li><li>标签具有很好的可移植性、可维护性和可重用性；</li><li>避免了对 Scriptlet（小脚本）的使用（很多公司的项目开发都不允许在 JSP 中书写小脚本）。</li></ul><p>自定义 JSP 标签包括以下几个步骤：</p><ol><li>编写一个 Java 类实现实现 <code>Tag</code>/<code>BodyTag</code>/<code>IterationTag</code> 接口（开发中通常不直接实现这些接口而是继承 <code>TagSupport</code>/<code>BodyTagSupport</code>/<code>SimpleTagSupport</code> 类，这是对缺省适配模式的应用），重写 <code>doStartTag()</code>、<code>doEndTag()</code> 等方法，定义标签要完成的功能；</li><li>编写扩展名为 <code>tld</code> 的标签描述文件对自定义标签进行部署，tld 文件通常放在 WEB-INF 文件夹下或其子目录中；</li><li>在 JSP 页面中使用 <code>taglib</code> 指令引用该标签库。</li></ol><p>下面是一个自定义标签库的例子。</p><ol><li><p>标签类源代码 TimeTag.java：</p><p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.tags;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.text.SimpleDateFormat;</span><br><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.jsp.JspException;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.jsp.JspWriter;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.jsp.tagext.TagSupport;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TimeTag</span> <span class="keyword">extends</span> <span class="title">TagSupport</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">1L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String format = <span class="string">&quot;yyyy-MM-dd hh:mm:ss&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> String foreColor = <span class="string">&quot;black&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> String backColor = <span class="string">&quot;white&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">doStartTag</span><span class="params">()</span> <span class="keyword">throws</span> JspException </span>&#123;</span><br><span class="line">        SimpleDateFormat sdf = <span class="keyword">new</span> SimpleDateFormat(format);</span><br><span class="line">        JspWriter writer = pageContext.getOut();</span><br><span class="line">        StringBuilder sb = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">        sb.append(String.format(<span class="string">&quot;&lt;span style=&#x27;color:%s;background-color:%s&#x27;&gt;%s&lt;/span&gt;&quot;</span>,</span><br><span class="line">            foreColor, backColor, sdf.format(<span class="keyword">new</span> Date())));</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          writer.print(sb.toString());</span><br><span class="line">        &#125; <span class="keyword">catch</span>(IOException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> SKIP_BODY;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setFormat</span><span class="params">(String format)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.format = format;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setForeColor</span><span class="params">(String foreColor)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.foreColor = foreColor;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBackColor</span><span class="params">(String backColor)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.backColor = backColor;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li><li><p>编写标签库描述文件 my.tld：</p><p> <figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">taglib</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://java.sun.com/xml/ns/j2ee&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://java.sun.com/xml/ns/j2ee </span></span></span><br><span class="line"><span class="tag"><span class="string">    http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">version</span>=<span class="string">&quot;2.0&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">description</span>&gt;</span>定义标签库<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">tlib-version</span>&gt;</span>1.0<span class="tag">&lt;/<span class="name">tlib-version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">short-name</span>&gt;</span>MyTag<span class="tag">&lt;/<span class="name">short-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">tag</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">name</span>&gt;</span>time<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">tag-class</span>&gt;</span>com.jackfrued.tags.TimeTag<span class="tag">&lt;/<span class="name">tag-class</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">body-content</span>&gt;</span>empty<span class="tag">&lt;/<span class="name">body-content</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">attribute</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">name</span>&gt;</span>format<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">required</span>&gt;</span>false<span class="tag">&lt;/<span class="name">required</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">attribute</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">attribute</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">name</span>&gt;</span>foreColor<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">attribute</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">attribute</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">name</span>&gt;</span>backColor<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">attribute</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">tag</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">taglib</span>&gt;</span></span><br></pre></td></tr></table></figure></p></li><li><p>在 JSP 页面中使用自定义标签：</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">&lt;%@ page pageEncoding=<span class="string">&quot;UTF-8&quot;</span>%&gt;</span><br><span class="line">&lt;%@ taglib prefix=<span class="string">&quot;my&quot;</span> uri=<span class="string">&quot;/WEB-INF/tld/my.tld&quot;</span> %&gt;</span><br><span class="line">&lt;%</span><br><span class="line">String path = request.getContextPath();</span><br><span class="line">String basePath = request.getScheme() + <span class="string">&quot;://&quot;</span> + request.getServerName() + <span class="string">&quot;:&quot;</span> + request.getServerPort() + path + <span class="string">&quot;/&quot;</span>;</span><br><span class="line">%&gt;</span><br><span class="line"></span><br><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">  &lt;head&gt;</span><br><span class="line">    &lt;base href=<span class="string">&quot;&lt;%=basePath%&gt;&quot;</span>&gt;</span><br><span class="line">    &lt;title&gt;首页&lt;/title&gt;</span><br><span class="line">    &lt;style type=<span class="string">&quot;text/css&quot;</span>&gt;</span><br><span class="line">        * &#123; font-family: <span class="string">&quot;Arial&quot;</span>; font-size:72px; &#125;</span><br><span class="line">    &lt;/style&gt;</span><br><span class="line">  &lt;/head&gt;</span><br><span class="line"></span><br><span class="line">  &lt;body&gt;</span><br><span class="line">    &lt;my:time format=<span class="string">&quot;yyyy-MM-dd&quot;</span> backColor=<span class="string">&quot;blue&quot;</span> foreColor=<span class="string">&quot;yellow&quot;</span>/&gt;</span><br><span class="line">  &lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure></p></li></ol><p><strong>注意：</strong>如果要将自定义的标签库发布成 JAR 文件，需要将标签库描述文件（tld 文件）放在 JAR 文件的 META-INF 目录下，可以用 JDK 中的 jar 工具完成 JAR 文件的生成，如果不清楚如何操作，可以请教<a href="https://www.google.com/">谷老师</a>和<a href="https://www.baidu.com/">百老师</a>。</p><h2 id="说一下表达式语言（EL）的隐式对象及其作用。"><a href="#说一下表达式语言（EL）的隐式对象及其作用。" class="headerlink" title="说一下表达式语言（EL）的隐式对象及其作用。"></a>说一下表达式语言（EL）的隐式对象及其作用。</h2><p>答：EL 的隐式对象包括：pageContext、initParam（访问上下文参数）、param（访问请求参数）、paramValues、header（访问请求头）、headerValues、cookie（访问 cookie）、applicationScope（访问 application 作用域）、sessionScope（访问 session 作用域）、requestScope（访问 request 作用域）、pageScope（访问 page 作用域）。</p><p>用法如下所示：</p><p><figure class="highlight jsp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$&#123;pageContext.request.method&#125;</span><br><span class="line">$&#123;pageContext[<span class="string">&quot;request&quot;</span>][<span class="string">&quot;method&quot;</span>]&#125;</span><br><span class="line">$&#123;pageContext.request[<span class="string">&quot;method&quot;</span>]&#125;</span><br><span class="line">$&#123;pageContext[<span class="string">&quot;request&quot;</span>].method&#125;</span><br><span class="line">$&#123;initParam.defaultEncoding&#125;</span><br><span class="line">$&#123;header[<span class="string">&quot;accept-language&quot;</span>]&#125;</span><br><span class="line">$&#123;headerValues[<span class="string">&quot;accept-language&quot;</span>][<span class="number">0</span>]&#125;</span><br><span class="line">$&#123;cookie.jsessionid.value&#125;</span><br><span class="line">$&#123;sessionScope.loginUser.username&#125;</span><br></pre></td></tr></table></figure></p><p><strong>补充：</strong>表达式语言的 <code>.</code> 和 <code>[]</code> 运算作用是一致的，唯一的差别在于如果访问的属性名不符合 Java 标识符命名规则，例如上面的 <code>accept-language</code> 就不是一个有效的 Java 标识符，那么这时候就只能用 <code>[]</code> 运算符而不能使用 <code>.</code> 运算符获取它的值。</p><h2 id="表达式语言（EL）支持哪些运算符？"><a href="#表达式语言（EL）支持哪些运算符？" class="headerlink" title="表达式语言（EL）支持哪些运算符？"></a>表达式语言（EL）支持哪些运算符？</h2><p>答：除了 <code>.</code> 和 <code>[]</code> 运算符，EL 还提供了：</p><ul><li>算术运算符：<code>+</code>、<code>-</code>、<code>*</code>、<code>/</code> 或 <code>div</code>、<code>%</code> 或 <code>mod</code>；</li><li>关系运算符：<code>==</code> 或 <code>eq</code>、<code>!=</code> 或 <code>ne</code>、<code>&gt;</code> 或 <code>gt</code>、<code>&gt;=</code> 或 <code>ge</code>、<code>&lt;</code> 或 <code>lt</code>、<code>&lt;=</code> 或 <code>le</code>；</li><li>逻辑运算符：<code>&amp;&amp;</code> 或 <code>and</code>、<code>||</code> 或 <code>or</code>、<code>!</code> 或 <code>not</code>；</li><li>条件运算符：<code>$&#123;statement? A : B&#125;</code>（跟 Java 的条件运算符类似）；</li><li><code>empty</code> 运算符：检查一个值是否为 <code>null</code> 或者 <code>空</code>（数组长度为 0 或集合中没有元素也返回 <code>true</code>）。</li></ul><h2 id="Java-Web-开发的-Model-1-和-Model-2-分别指的是什么？"><a href="#Java-Web-开发的-Model-1-和-Model-2-分别指的是什么？" class="headerlink" title="Java Web 开发的 Model 1 和 Model 2 分别指的是什么？"></a>Java Web 开发的 Model 1 和 Model 2 分别指的是什么？</h2><p>答：Model 1 是以页面为中心的 Java Web 开发，使用 JSP+JavaBean 技术将页面显示逻辑和业务逻辑处理分开，JSP 实现页面显示，JavaBean 对象用来保存数据和实现业务逻辑。Model 2 是基于 MVC（模型-视图-控制器，Model-View-Controller）架构模式的开发模型，实现了模型和视图的彻底分离，利于团队开发和代码复用，如下图所示。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/113.png" alt="MVC 架构模式"></p><h2 id="Servlet-3-中的异步处理指的是什么？"><a href="#Servlet-3-中的异步处理指的是什么？" class="headerlink" title="Servlet 3 中的异步处理指的是什么？"></a>Servlet 3 中的异步处理指的是什么？</h2><p>答：在 Servlet 3 中引入了一项新的技术可以让 Servlet 异步处理请求。有人可能会质疑，既然都有多线程了，还需要异步处理请求吗？答案是肯定需要的，因为如果一个任务处理时间相当长，那么 Servlet 或 Filter 会一直占用着请求处理线程直到任务结束，随着并发用户的增加，容器将会遭遇线程超出的风险，这这种情况下很多的请求将会被堆积起来而后续的请求可能会遭遇拒绝服务，直到有资源可以处理请求为止。异步特性可以帮助应用节省容器中的线程，特别适合执行时间长而且用户需要得到结果的任务，如果用户不需要得到结果则直接将一个 Runnable 对象交给 Executor 并立即返回即可。</p><p><strong>补充：</strong>多线程在 Java 诞生初期无疑是一个亮点，而 Servlet 单实例多线程的工作方式也曾为其赢得美名，然而技术的发展往往会颠覆我们很多的认知，就如同当年爱因斯坦的相对论颠覆了牛顿的经典力学一般。事实上，异步处理绝不是 Serlvet 3 首创，如果你了解 Node.js 的话，对 Servlet 3 的这个重要改进就不以为奇了。</p><p>下面是一个支持异步处理请求的 Servlet 的例子。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.AsyncContext;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletException;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.annotation.WebServlet;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServlet;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletRequest;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletResponse;</span><br><span class="line"></span><br><span class="line"><span class="meta">@WebServlet(urlPatterns = &#123;&quot;/async&quot;&#125;, asyncSupported = true)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AsyncServlet</span> <span class="keyword">extends</span> <span class="title">HttpServlet</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">1L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doGet</span><span class="params">(HttpServletRequest req, HttpServletResponse resp)</span> </span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> ServletException, IOException </span>&#123;</span><br><span class="line">        <span class="comment">// 开启 Tomcat 异步 Servlet 支持</span></span><br><span class="line">        req.setAttribute(<span class="string">&quot;org.apache.catalina.ASYNC_SUPPORTED&quot;</span>, <span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">final</span> AsyncContext ctx = req.startAsync();  <span class="comment">// 启动异步处理的上下文</span></span><br><span class="line">        <span class="comment">// ctx.setTimeout(30000);</span></span><br><span class="line">        ctx.start(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="comment">// 在此处添加异步处理的代码</span></span><br><span class="line"></span><br><span class="line">                ctx.complete();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="如何在基于-Java-的-Web-项目中实现文件上传和下载？"><a href="#如何在基于-Java-的-Web-项目中实现文件上传和下载？" class="headerlink" title="如何在基于 Java 的 Web 项目中实现文件上传和下载？"></a>如何在基于 Java 的 Web 项目中实现文件上传和下载？</h2><p>答：在 Sevlet 3 以前，Servlet API 中没有支持上传功能的 API，因此要实现上传功能需要引入第三方工具从 POST 请求中获得上传的附件或者通过自行处理输入流来获得上传的文件，我们推荐使用 Apache 的 commons-fileupload。</p><p>从 Servlet 3 开始，文件上传变得无比简单，相信看看下面的例子一切都清楚了。</p><p>上传页面 index.jsp：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">&lt;%@ page pageEncoding=<span class="string">&quot;utf-8&quot;</span>%&gt;</span><br><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">&lt;meta http-equiv=<span class="string">&quot;Content-Type&quot;</span> content=<span class="string">&quot;text/html; charset=UTF-8&quot;</span>&gt;</span><br><span class="line">&lt;title&gt;Photo Upload&lt;/title&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">&lt;h1&gt;Select your photo and upload&lt;/h1&gt;</span><br><span class="line">&lt;hr/&gt;</span><br><span class="line">&lt;div style=&quot;color:red;font-size:14px;&quot;&gt;$&#123;hint&#125;&lt;/div&gt;</span><br><span class="line">&lt;form action=<span class="string">&quot;UploadServlet&quot;</span> method=<span class="string">&quot;post&quot;</span> enctype=<span class="string">&quot;multipart/form-data&quot;</span>&gt;</span><br><span class="line">    Photo file: &lt;input type=<span class="string">&quot;file&quot;</span> name=<span class="string">&quot;photo&quot;</span> /&gt;</span><br><span class="line">    &lt;input type=<span class="string">&quot;submit&quot;</span> value=<span class="string">&quot;Upload&quot;</span> /&gt;</span><br><span class="line">&lt;/form&gt;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure></p><p>支持上传的 Servlet：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.jackfrued.servlet;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletException;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.annotation.MultipartConfig;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.annotation.WebServlet;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServlet;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletRequest;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletResponse;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.Part;</span><br><span class="line"></span><br><span class="line"><span class="meta">@WebServlet(&quot;/UploadServlet&quot;)</span></span><br><span class="line"><span class="meta">@MultipartConfig</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UploadServlet</span> <span class="keyword">extends</span> <span class="title">HttpServlet</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">1L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">doPost</span><span class="params">(HttpServletRequest request,</span></span></span><br><span class="line"><span class="function"><span class="params">            HttpServletResponse response)</span> <span class="keyword">throws</span> ServletException, IOException </span>&#123;</span><br><span class="line">        <span class="comment">// 可以用 request.getPart() 方法获得名为 photo 的上传附件</span></span><br><span class="line">        <span class="comment">// 也可以用 request.getParts() 获得所有上传附件（多文件上传）</span></span><br><span class="line">        <span class="comment">// 然后通过循环分别处理每一个上传的文件</span></span><br><span class="line">        Part part = request.getPart(<span class="string">&quot;photo&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (part != <span class="keyword">null</span> &amp;&amp; part.getSubmittedFileName().length() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 用 ServletContext 对象的 getRealPath()方法获得上传文件夹的绝对路径</span></span><br><span class="line">            String savePath = request.getServletContext().getRealPath(<span class="string">&quot;/upload&quot;</span>);</span><br><span class="line">            <span class="comment">// Servlet 3.1 规范中可以用 Part 对象的 getSubmittedFileName()方法获得上传的文件名</span></span><br><span class="line">            <span class="comment">// 更好的做法是为上传的文件进行重命名（避免同名文件的相互覆盖）</span></span><br><span class="line">            part.write(savePath + <span class="string">&quot;/&quot;</span> + part.getSubmittedFileName());</span><br><span class="line">            request.setAttribute(<span class="string">&quot;hint&quot;</span>, <span class="string">&quot;Upload Successfully!&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            request.setAttribute(<span class="string">&quot;hint&quot;</span>, <span class="string">&quot;Upload failed!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 跳转回到上传页面</span></span><br><span class="line">        request.getRequestDispatcher(<span class="string">&quot;index.jsp&quot;</span>).forward(request, response);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="服务器收到用户提交的表单数据，到底是调用-Servlet-的-doGet-还是-doPost-方法？"><a href="#服务器收到用户提交的表单数据，到底是调用-Servlet-的-doGet-还是-doPost-方法？" class="headerlink" title="服务器收到用户提交的表单数据，到底是调用 Servlet 的 doGet() 还是 doPost() 方法？"></a>服务器收到用户提交的表单数据，到底是调用 Servlet 的 <code>doGet()</code> 还是 <code>doPost()</code> 方法？</h2><p>答：HTML 的 <code>&lt;form&gt;</code> 元素有一个 <code>method</code> 属性，用来指定提交表单的方式，其值可以是 <code>get</code> 或 <code>post</code>。我们自定义的 Servlet 一般情况下会重写 <code>doGet()</code> 或 <code>doPost()</code> 两个方法之一或全部，如果是 <code>GET</code> 请求就调用 <code>doGet()</code> 方法，如果是 <code>POST</code> 请求就调用 <code>doPost()</code> 方法，那为什么为什么这样呢？我们自定义的 Servlet 通常继承自 <code>HttpServlet</code>，<code>HttpServlet</code> 继承自 <code>GenericServlet</code> 并重写了其中的 <code>service()</code> 方法，这个方法是 Servlet 接口中定义的。<code>HttpServlet</code> 重写的 <code>service()</code> 方法会先获取用户请求的方法，然后根据请求方法调用 <code>doGet()</code>、<code>doPost()</code>、<code>doPut()</code>、<code>doDelete()</code> 等方法，如果在自定义 Servlet 中重写了这些方法，那么显然会调用重写过的（自定义的）方法，这显然是对模板方法模式的应用（如果不理解，请参考阎宏博士的《Java 与模式》一书的第 37 章）。当然，自定义 Servlet 中也可以直接重写 <code>service()</code> 方法，那么不管是哪种方式的请求，都可以通过自己的代码进行处理，这对于不区分请求方法的场景比较合适。</p><h2 id="JSP-中的静态包含和动态包含有什么区别？"><a href="#JSP-中的静态包含和动态包含有什么区别？" class="headerlink" title="JSP 中的静态包含和动态包含有什么区别？"></a>JSP 中的静态包含和动态包含有什么区别？</h2><p>答：静态包含是通过 JSP 的 <code>include</code> 指令包含页面，动态包含是通过 JSP 标准动作 <code>&lt;jsp:forward&gt;</code> 包含页面。</p><ul><li><p>静态包含是编译时包含，如果包含的页面不存在则会产生编译错误，而且两个页面的 <code>contentType</code> 属性应保持一致，因为两个页面会合二为一，只产生一个 class 文件，因此被包含页面发生的变动再包含它的页面更新前不会得到更新。</p></li><li><p>动态包含是运行时包含，可以向被包含的页面传递参数，包含页面和被包含页面是独立的，会编译出两个 class 文件，如果被包含的页面不存在，不会产生编译错误，也不影响页面其他部分的执行。代码如下所示：</p></li></ul><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&lt;%-- 静态包含 --%&gt;</span><br><span class="line">&lt;%@ include file=<span class="string">&quot;...&quot;</span> %&gt;</span><br><span class="line"></span><br><span class="line">&lt;%-- 动态包含 --%&gt;</span><br><span class="line">&lt;jsp:include page=<span class="string">&quot;...&quot;</span>&gt;</span><br><span class="line">    &lt;jsp:param name=<span class="string">&quot;...&quot;</span> value=<span class="string">&quot;...&quot;</span> /&gt;</span><br><span class="line">&lt;/jsp:include&gt;</span><br></pre></td></tr></table></figure></p><h2 id="Servlet-中如何获取用户提交的查询参数或表单数据？"><a href="#Servlet-中如何获取用户提交的查询参数或表单数据？" class="headerlink" title="Servlet 中如何获取用户提交的查询参数或表单数据？"></a>Servlet 中如何获取用户提交的查询参数或表单数据？</h2><p>答：可以通过请求对象 <code>HttpServletRequest</code> 的 <code>getParameter()</code> 方法通过参数名获得参数值。如果有包含多个值的参数（例如复选框），可以通过请求对象的 <code>getParameterValues()</code> 方法获得。当然也可以通过请求对象的 <code>getParameterMap()</code> 获得一个参数名和参数值的映射（Map）。</p><h2 id="Servlet-中如何获取用户配置的初始化参数以及服务器上下文参数？"><a href="#Servlet-中如何获取用户配置的初始化参数以及服务器上下文参数？" class="headerlink" title="Servlet 中如何获取用户配置的初始化参数以及服务器上下文参数？"></a>Servlet 中如何获取用户配置的初始化参数以及服务器上下文参数？</h2><p>答：可以通过重写 Servlet 接口的 <code>init(ServletConfig)</code> 方法并通过 <code>ServletConfig</code> 对象的 <code>getInitParameter()</code> 方法来获取 Servlet 的初始化参数。可以通过 <code>ServletConfig</code> 对象的 <code>getServletContext()</code> 方法获取 <code>ServletContext</code> 对象，并通过该对象的 <code>getInitParameter()</code> 方法来获取服务器上下文参数。当然，<code>ServletContext</code> 对象也在处理用户请求的方法（如 <code>doGet()</code> 方法）中通过请求对象的 <code>getServletContext()</code>方法来获得。</p><h2 id="如何设置请求的编码以及响应内容的类型？"><a href="#如何设置请求的编码以及响应内容的类型？" class="headerlink" title="如何设置请求的编码以及响应内容的类型？"></a>如何设置请求的编码以及响应内容的类型？</h2><p>答：通过请求对象 <code>ServletRequest</code> 的 <code>setCharacterEncoding(String)</code> 方法可以设置请求的编码，其实要彻底解决乱码问题就应该让页面、服务器、请求和响应、Java 程序都使用统一的编码，最好的选择当然是 <code>UTF-8</code>；通过响应对象 <code>ServletResponse</code> 的 <code>setContentType(String)</code> 方法可以设置响应内容的类型，当然也可以通过 <code>HttpServletResponsed</code> 对象的 <code>setHeader(String, String)</code> 方法来设置。</p><p><strong>说明：</strong>现在如果还有公司在面试的时候问 JSP 的声明标记、表达式标记、小脚本标记这些内容的话，这样的公司也不用去了，其实 JSP 内置对象、JSP 指令这些东西基本上都可以忘却了，关于 Java Web 开发的相关知识，可以看一下我的 <a target="_blank" rel="noopener" href="/images/post/java_interview/servlet_jsp.png" one-link-mark="yes">Servlet&amp;JSP 思维导图</a>，上面有完整的知识点的罗列。</p><h2 id="解释一下网络应用的模式及其特点。"><a href="#解释一下网络应用的模式及其特点。" class="headerlink" title="解释一下网络应用的模式及其特点。"></a>解释一下网络应用的模式及其特点。</h2><p>答：典型的网络应用模式大致有三类：B/S、C/S、P2P。其中 B 代表浏览器（Browser）、C 代表客户端（Client）、S 代表服务器（Server），P2P 是对等模式，不区分客户端和服务器。B/S 应用模式中可以视为特殊的 C/S 应用模式，只是将 C/S 应用模式中的特殊的客户端换成了浏览器，因为几乎所有的系统上都有浏览器，那么只要打开浏览器就可以使用应用，没有安装、配置、升级客户端所带来的各种开销。P2P 应用模式中，成千上万台彼此连接的计算机都处于对等的地位，整个网络一般来说不依赖专用的集中服务器。网络中的每一台计算机既能充当网络服务的请求者，又对其它计算机的请求作出响应，提供资源和服务。通常这些资源和服务包括：信息的共享和交换、计算资源（如 CPU 的共享）、存储共享（如缓存和磁盘空间的使用）等，这种应用模式最大的阻力安全性、版本等问题，目前有很多应用都混合使用了多种应用模型，最常见的网络视频应用，它几乎把三种模式都用上了。</p><p><strong>补充：</strong>此题要跟“电子商务模式”区分开，因为有很多人被问到这个问题的时候马上想到的是 B2B（如阿里巴巴）、B2C（如当当、亚马逊、京东）、C2C（如淘宝、拍拍）、C2B（如威客）、O2O（如美团、饿了么）。对于这类问题，可以去百度上面科普一下。</p><h2 id="什么是-Web-Service（Web-服务）？"><a href="#什么是-Web-Service（Web-服务）？" class="headerlink" title="什么是 Web Service（Web 服务）？"></a>什么是 Web Service（Web 服务）？</h2><p>答：从表面上看，Web Service 就是一个应用程序，它向外界暴露出一个能够通过 Web 进行调用的 API。这就是说，你能够用编程的方法透明的调用这个应用程序，不需要了解它的任何细节，跟你使用的编程语言也没有关系。例如可以创建一个提供天气预报的 Web Service，那么无论你用哪种编程语言开发的应用都可以通过调用它的 API 并传入城市信息来获得该城市的天气预报。之所以称之为 Web Service，是因为它基于 HTTP 协议传输数据，这使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件，就可相互交换数据或集成。</p><p><strong>补充：</strong>这里必须要提及的一个概念是 SOA（Service-Oriented Architecture，面向服务的架构），SOA 是一种思想，它将应用程序的不同功能单元通过中立的契约联系起来，独立于硬件平台、操作系统和编程语言，使得各种形式的功能单元能够更好的集成。显然，Web Service 是 SOA 的一种较好的解决方案，它更多的是一种标准，而不是一种具体的技术。</p><h2 id="概念解释：SOAP、WSDL、UDDI。"><a href="#概念解释：SOAP、WSDL、UDDI。" class="headerlink" title="概念解释：SOAP、WSDL、UDDI。"></a>概念解释：SOAP、WSDL、UDDI。</h2><p>答：</p><ul><li>SOAP：简单对象访问协议（Simple Object Access Protocol），是 Web Service 中交换数据的一种协议规范。</li><li>WSDL：Web 服务描述语言（Web Service Description Language），它描述了 Web 服务的公共接口。这是一个基于 XML 的关于如何与 Web 服务通讯和使用的服务描述；也就是描述与目录中列出的 Web 服务进行交互时需要绑定的协议和信息格式。通常采用抽象语言描述该服务支持的操作和信息，使用的时候再将实际的网络协议和信息格式绑定给该服务。</li><li>UDDI：统一描述、发现和集成（Universal Description, Discovery and Integration），它是一个基于 XML 的跨平台的描述规范，可以使世界范围内的企业在互联网上发布自己所提供的服务。简单的说，UDDI 是访问各种 WSDL 的一个门面（可以参考设计模式中的门面模式）。</li></ul><h2 id="Java-规范中和-Web-Service-相关的规范有哪些？"><a href="#Java-规范中和-Web-Service-相关的规范有哪些？" class="headerlink" title="Java 规范中和 Web Service 相关的规范有哪些？"></a>Java 规范中和 Web Service 相关的规范有哪些？</h2><p>答：Java 规范中和 Web Service 相关的有三个：</p><ul><li>JAX-WS(JSR 224)：这个规范是早期的基于 SOAP 的 Web Service 规范 JAX-RPC 的替代版本，它并不提供向下兼容性，因为 RPC 样式的 WSDL 以及相关的 API 已经在 Java EE5 中被移除了。WS-MetaData 是 JAX-WS 的依赖规范，提供了基于注解配置 Web Service 和 SOAP 消息的相关 API。</li><li>JAXM(JSR 67)：定义了发送和接收消息所需的 API，相当于 Web Service 的服务器端。</li><li>JAX-RS(JSR 311 &amp; JSR 339 &amp; JSR 370)：是 Java 针对 REST（Representation State Transfer）架构风格制定的一套 Web Service 规范。REST 是一种软件架构模式，是一种风格，它不像 SOAP 那样本身承载着一种消息协议， (两种风格的 Web Service 均采用了 HTTP 做传输协议，因为 HTTP 协议能穿越防火墙，Java 的远程方法调用（RMI）等是重量级协议，通常不能穿越防火墙），因此可以将 REST 视为基于 HTTP 协议的软件架构。REST 中最重要的两个概念是资源定位和资源操作，而 HTTP 协议恰好完整的提供了这两个点。HTTP 协议中的 URI 可以完成资源定位，而 GET、POST、OPTION、DELETE 方法可以完成资源操作。因此 REST 完全依赖 HTTP 协议就可以完成 Web Service，而不像 SOAP 协议那样只利用了 HTTP 的传输特性，定位和操作都是由 SOAP 协议自身完成的，也正是由于 SOAP 消息的存在使得基于 SOAP 的 Web Service 显得笨重而逐渐被淘汰。</li></ul><h2 id="介绍一下你了解的-Java-领域的-Web-Service-框架。"><a href="#介绍一下你了解的-Java-领域的-Web-Service-框架。" class="headerlink" title="介绍一下你了解的 Java 领域的 Web Service 框架。"></a>介绍一下你了解的 Java 领域的 Web Service 框架。</h2><p>答：Java 领域的 Web Service 框架很多，包括 Axis2（Axis 的升级版本）、Jersey（RESTful 的 Web Service 框架）、CXF（XFire 的延续版本）、Hessian、Turmeric、JBoss SOA 等，其中绝大多数都是开源框架。</p><p><strong>注意：</strong>面试被问到这类问题的时候一定选择自己用过的最熟悉的作答，如果之前没有了解过就应该在面试前花一些时间了解其中的两个，并比较其优缺点，这样才能在面试时给出一个漂亮的答案。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Java/">Java</category>
      
      
      <comments>https://blog.eurkon.com/post/d24a315e.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Java 面试题解析（设计模式）</title>
      <link>https://blog.eurkon.com/post/1cfbd3b3.html</link>
      <guid>https://blog.eurkon.com/post/1cfbd3b3.html</guid>
      <pubDate>Sun, 21 Mar 2021 04:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;简述一下面向对象的-quot-六原则一法则-quot-。&quot;&gt;&lt;a href=&quot;#简述一下面向对象的-quot-六原则一法则-quot-。&quot; class=&quot;headerlink&quot; title=&quot;简述一下面向对象的&amp;quot;六原则一法则&amp;quot;。&quot;&gt;&lt;/a&gt;简述</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="简述一下面向对象的-quot-六原则一法则-quot-。"><a href="#简述一下面向对象的-quot-六原则一法则-quot-。" class="headerlink" title="简述一下面向对象的&quot;六原则一法则&quot;。"></a>简述一下面向对象的&quot;六原则一法则&quot;。</h2><p>答：</p><ul><li><p><strong>单一职责原则：一个类只做它该做的事情。</strong>（单一职责原则想表达的就是&quot;高内聚&quot;，写代码最终极的原则只有六个字“高内聚、低耦合”，就如同葵花宝典或辟邪剑谱的中心思想就八个字“欲练此功必先自宫”，所谓的高内聚就是一个代码模块只完成一项功能，在面向对象中，如果只让一个类完成它该做的事，而不涉及与它无关的领域就是践行了高内聚的原则，这个类就只有单一职责。我们都知道一句话叫“因为专注，所以专业”，一个对象如果承担太多的职责，那么注定它什么都做不好。这个世界上任何好的东西都有两个特征，一个是功能单一，好的相机绝对不是电视购物里面卖的那种一个机器有一百多种功能的，它基本上只能照相；另一个是模块化，好的自行车是组装车，从减震叉、刹车到变速器，所有的部件都是可以拆卸和重新组装的，好的乒乓球拍也不是成品拍，一定是底板和胶皮可以拆分和自行组装的，一个好的软件系统，它里面的每个功能模块也应该是可以轻易的拿到其他系统中使用的，这样才能实现软件复用的目标。）</p></li><li><p><strong>开闭原则：软件实体应当对扩展开放，对修改关闭。</strong>（在理想的状态下，当我们需要为一个软件系统增加新功能时，只需要从原来的系统派生出一些新类就可以，不需要修改原来的任何一行代码。要做到开闭有两个要点：(1)抽象是关键，一个系统中如果没有抽象类或接口系统就没有扩展点；(2)封装可变性，将系统中的各种可变因素封装到一个继承结构中，如果多个可变因素混杂在一起，系统将变得复杂而换乱，如果不清楚如何封装可变性，可以参考《设计模式精解》一书中对桥梁模式的讲解的章节。）</p></li><li><p><strong>依赖倒转原则：面向接口编程。</strong>（该原则说得直白和具体一些就是声明方法的参数类型、方法的返回类型、变量的引用类型时，尽可能使用抽象类型而不用具体类型，因为抽象类型可以被它的任何一个子类型所替代，请参考下面的里氏替换原则。）<strong>里氏替换原则：任何时候都可以用子类型替换掉父类型。</strong>（关于里氏替换原则的描述，Barbara Liskov 女士的描述比这个要复杂得多，但简单的说就是能用父类型的地方就一定能使用子类型。里氏替换原则可以检查继承关系是否合理，如果一个继承关系违背了里氏替换原则，那么这个继承关系一定是错误的，需要对代码进行重构。例如让猫继承狗，或者狗继承猫，又或者让正方形继承长方形都是错误的继承关系，因为你很容易找到违反里氏替换原则的场景。需要注意的是：子类一定是增加父类的能力而不是减少父类的能力，因为子类比父类的能力更多，把能力多的对象当成能力少的对象来用当然没有任何问题。）</p></li><li><p><strong>接口隔离原则：接口要小而专，绝不能大而全。</strong>（臃肿的接口是对接口的污染，既然接口表示能力，那么一个接口只应该描述一种能力，接口也应该是高度内聚的。例如，琴棋书画就应该分别设计为四个接口，而不应设计成一个接口中的四个方法，因为如果设计成一个接口中的四个方法，那么这个接口很难用，毕竟琴棋书画四样都精通的人还是少数，而如果设计成四个接口，会几项就实现几个接口，这样的话每个接口被复用的可能性是很高的。Java 中的接口代表能力、代表约定、代表角色，能否正确的使用接口一定是编程水平高低的重要标识。）</p></li><li><p><strong>合成聚合复用原则：优先使用聚合或合成关系复用代码。</strong>（通过继承来复用代码是面向对象程序设计中被滥用得最多的东西，因为所有的教科书都无一例外的对继承进行了鼓吹从而误导了初学者，类与类之间简单的说有三种关系，Is-A 关系、Has-A 关系、Use-A 关系，分别代表继承、关联和依赖。其中，关联关系根据其关联的强度又可以进一步划分为关联、聚合和合成，但说白了都是 Has-A 关系，合成聚合复用原则想表达的是优先考虑 Has-A 关系而不是 Is-A 关系复用代码，原因嘛可以自己从百度上找到一万个理由，需要说明的是，即使在 Java 的 API 中也有不少滥用继承的例子，例如 Properties 类继承了 Hashtable 类，Stack 类继承了 Vector 类，这些继承明显就是错误的，更好的做法是在 Properties 类中放置一个 Hashtable 类型的成员并且将其键和值都设置为字符串来存储数据，而 Stack 类的设计也应该是在 Stack 类中放一个 Vector 对象来存储数据。记住：任何时候都不要继承工具类，工具是可以拥有并可以使用的，而不是拿来继承的。）</p></li><li><p><strong>迪米特法则：迪米特法则又叫最少知识原则，一个对象应当对其他对象有尽可能少的了解。</strong>（迪米特法则简单的说就是如何做到“低耦合”，门面模式和调停者模式就是对迪米特法则的践行。对于门面模式可以举一个简单的例子，你去一家公司洽谈业务，你不需要了解这个公司内部是如何运作的，你甚至可以对这个公司一无所知，去的时候只需要找到公司入口处的前台美女，告诉她们你要做什么，她们会找到合适的人跟你接洽，前台的美女就是公司这个系统的门面。再复杂的系统都可以为用户提供一个简单的门面，Java Web 开发中作为前端控制器的 Servlet 或 Filter 不就是一个门面吗，浏览器对服务器的运作方式一无所知，但是通过前端控制器就能够根据你的请求得到相应的服务。调停者模式也可以举一个简单的例子来说明，例如一台计算机，CPU、内存、硬盘、显卡、声卡各种设备需要相互配合才能很好的工作，但是如果这些东西都直接连接到一起，计算机的布线将异常复杂，在这种情况下，主板作为一个调停者的身份出现，它将各个设备连接在一起而不需要每个设备之间直接交换数据，这样就减小了系统的耦合度和复杂度，如下图所示。迪米特法则用通俗的话来将就是不要和陌生人打交道，如果真的需要，找一个自己的朋友，让他替你和陌生人打交道。）</p></li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/89_1.png" alt="迪米特法则"></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/89_2.png" alt="迪米特法则"></p><h2 id="简述一下你了解的设计模式。"><a href="#简述一下你了解的设计模式。" class="headerlink" title="简述一下你了解的设计模式。"></a>简述一下你了解的设计模式。</h2><p>答：所谓设计模式，就是一套被反复使用的代码设计经验的总结（情境中一个问题经过证实的一个解决方案）。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使人们可以更加简单方便的复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。</p><p>在 GoF 的《Design Patterns: Elements of Reusable Object-Oriented Software》中给出了三类（<strong>创建型</strong>[对类的实例化过程的抽象化]、<strong>结构型</strong>[描述如何将类或对象结合在一起形成更大的结构]、<strong>行为型</strong>[对在不同的对象之间划分责任和算法的抽象化]）共 23 种设计模式，包括：Abstract Factory（抽象工厂模式），Builder（建造者模式），Factory Method（工厂方法模式），Prototype（原始模型模式），Singleton（单例模式）；Facade（门面模式），Adapter（适配器模式），Bridge（桥梁模式），Composite（合成模式），Decorator（装饰模式），Flyweight（享元模式），Proxy（代理模式）；Command（命令模式），Interpreter（解释器模式），Visitor（访问者模式），Iterator（迭代子模式），Mediator（调停者模式），Memento（备忘录模式），Observer（观察者模式），State（状态模式），Strategy（策略模式），Template Method（模板方法模式），Chain Of Responsibility（责任链模式）。</p><p>面试被问到关于设计模式的知识时，可以拣最常用的作答，例如：</p><ul><li>工厂模式：工厂类可以根据条件生成不同的子类实例，这些子类有一个公共的抽象父类并且实现了相同的方法，但是这些方法针对不同的数据进行了不同的操作（多态方法）。当得到子类的实例后，开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。</li><li>代理模式：给一个对象提供一个代理对象，并由代理对象控制原对象的引用。实际开发中，按照使用目的的不同，代理可以分为：远程代理、虚拟代理、保护代理、Cache 代理、防火墙代理、同步化代理、智能引用代理。</li><li>适配器模式：把一个类的接口变换成客户端所期待的另一种接口，从而使原本因接口不匹配而无法在一起使用的类能够一起工作。</li><li>模板方法模式：提供一个抽象类，将部分逻辑以具体方法或构造器的形式实现，然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法（多态实现），从而实现不同的业务逻辑。</li></ul><p>除此之外，还可以讲讲上面提到的门面模式、桥梁模式、单例模式、装潢模式（Collections 工具类和 I/O 系统中都使用装潢模式）等，反正基本原则就是拣自己最熟悉的、用得最多的作答，以免言多必失。</p><h2 id="用-Java-写一个单例类。"><a href="#用-Java-写一个单例类。" class="headerlink" title="用 Java 写一个单例类。"></a>用 Java 写一个单例类。</h2><p>答：</p><ul><li><p>饿汉式单例</p><p>  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Singleton</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Singleton</span><span class="params">()</span></span>&#123;&#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Singleton instance = <span class="keyword">new</span> Singleton();</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title">getInstance</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> instance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li><li><p>懒汉式单例</p><p>  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Singleton</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Singleton instance = <span class="keyword">null</span>;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Singleton</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">synchronized</span> Singleton <span class="title">getInstance</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (instance == <span class="keyword">null</span>) instance ＝ <span class="keyword">new</span> Singleton();</span><br><span class="line">        <span class="keyword">return</span> instance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li></ul><p><strong>注意：</strong>实现一个单例有两点注意事项，(1)将构造器私有，不允许外界通过构造器创建对象；(2)通过公开的静态方法向外界返回类的唯一实例。这里有一个问题可以思考：Spring 的 IoC 容器可以为普通的类创建单例，它是怎么做到的呢？</p><h2 id="什么是-UML？"><a href="#什么是-UML？" class="headerlink" title="什么是 UML？"></a>什么是 UML？</h2><p>答：UML 是统一建模语言（Unified Modeling Language）的缩写，它发表于 1997 年，综合了当时已经存在的面向对象的建模语言、方法和过程，是一个支持模型化和软件系统开发的图形化语言，为软件开发的所有阶段提供模型化和可视化支持。使用 UML 可以帮助沟通与交流，辅助应用设计和文档的生成，还能够阐释系统的结构和行为。</p><h2 id="UML-中有哪些常用的图？"><a href="#UML-中有哪些常用的图？" class="headerlink" title="UML 中有哪些常用的图？"></a>UML 中有哪些常用的图？</h2><p>答：UML 定义了多种图形化的符号来描述软件系统部分或全部的静态结构和动态结构，包括：用例图（use case diagram）、类图（class diagram）、时序图（sequence diagram）、协作图（collaboration diagram）、状态图（statechart diagram）、活动图（activity diagram）、构件图（component diagram）、部署图（deployment diagram）等。在这些图形化符号中，有三种图最为重要，分别是：用例图（用来捕获需求，描述系统的功能，通过该图可以迅速的了解系统的功能模块及其关系）、类图（描述类以及类与类之间的关系，通过该图可以快速了解系统）、时序图（描述执行特定任务时对象之间的交互关系以及执行顺序，通过该图可以了解对象能接收的消息也就是说对象能够向外界提供的服务）。</p><ul><li>用例图：</li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/93_1.png" alt="用例图："></p><ul><li>类图：</li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/93_2.png" alt="类图："></p><ul><li>时序图：</li></ul><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/93_3.png" alt="时序图"></p><h2 id="用-Java-写一个冒泡排序。"><a href="#用-Java-写一个冒泡排序。" class="headerlink" title="用 Java 写一个冒泡排序。"></a>用 Java 写一个冒泡排序。</h2><p>答：冒泡排序几乎是个程序员都写得出来，但是面试的时候如何写一个逼格高的冒泡排序却不是每个人都能做到，下面提供一个参考代码：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Comparator;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 排序器接口(策略模式: 将算法封装到具有共同接口的独立的类中使得它们可以相互替换)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Sorter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 排序</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> list 待排序的数组</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">public</span> &lt;T extends Comparable&lt;T&gt;&gt; <span class="function"><span class="keyword">void</span> <span class="title">sort</span><span class="params">(T[] list)</span></span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 排序</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> list 待排序的数组</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> comp 比较两个对象的比较器</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">public</span> &lt;T&gt; <span class="function"><span class="keyword">void</span> <span class="title">sort</span><span class="params">(T[] list, Comparator&lt;T&gt; comp)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Comparator;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 冒泡排序</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BubbleSorter</span> <span class="keyword">implements</span> <span class="title">Sorter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> &lt;T extends Comparable&lt;T&gt;&gt; <span class="function"><span class="keyword">void</span> <span class="title">sort</span><span class="params">(T[] list)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">boolean</span> swapped = <span class="keyword">true</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>, len = list.length; i &lt; len &amp;&amp; swapped; ++i) &#123;</span><br><span class="line">            swapped = <span class="keyword">false</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; len - i; ++j) &#123;</span><br><span class="line">                <span class="keyword">if</span> (list[j].compareTo(list[j + <span class="number">1</span>]) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    T temp = list[j];</span><br><span class="line">                    list[j] = list[j + <span class="number">1</span>];</span><br><span class="line">                    list[j + <span class="number">1</span>] = temp;</span><br><span class="line">                    swapped = <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> &lt;T&gt; <span class="function"><span class="keyword">void</span> <span class="title">sort</span><span class="params">(T[] list, Comparator&lt;T&gt; comp)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">boolean</span> swapped = <span class="keyword">true</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>, len = list.length; i &lt; len &amp;&amp; swapped; ++i) &#123;</span><br><span class="line">            swapped = <span class="keyword">false</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; len - i; ++j) &#123;</span><br><span class="line">                <span class="keyword">if</span> (comp.compare(list[j], list[j + <span class="number">1</span>]) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    T temp = list[j];</span><br><span class="line">                    list[j] = list[j + <span class="number">1</span>];</span><br><span class="line">                    list[j + <span class="number">1</span>] = temp;</span><br><span class="line">                    swapped = <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="用-Java-写一个折半查找。"><a href="#用-Java-写一个折半查找。" class="headerlink" title="用 Java 写一个折半查找。"></a>用 Java 写一个折半查找。</h2><p>答：折半查找，也称二分查找、二分搜索，是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始，如果中间元素正好是要查找的元素，则搜素过程结束；如果某一特定元素大于或者小于中间元素，则在数组大于或小于中间元素的那一半中查找，而且跟开始一样从中间元素开始比较。如果在某一步骤数组已经为空，则表示找不到指定的元素。这种搜索算法每一次比较都使搜索范围缩小一半，其时间复杂度是 O(logN)。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Comparator;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyUtil</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> &lt;T extends Comparable&lt;T&gt;&gt; <span class="function"><span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(T[] x, T key)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> binarySearch(x, <span class="number">0</span>, x.length- <span class="number">1</span>, key);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// 使用循环实现的二分查找</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="function"><span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(T[] x, T key, Comparator&lt;T&gt; comp)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">int</span> low = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">int</span> high = x.length - <span class="number">1</span>;</span><br><span class="line">      <span class="keyword">while</span> (low &lt;= high) &#123;</span><br><span class="line">          <span class="keyword">int</span> mid = (low + high) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">          <span class="keyword">int</span> cmp = comp.compare(x[mid], key);</span><br><span class="line">          <span class="keyword">if</span> (cmp &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            low= mid + <span class="number">1</span>;</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">else</span> <span class="keyword">if</span> (cmp &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            high= mid - <span class="number">1</span>;</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> mid;</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// 使用递归实现的二分查找</span></span><br><span class="line">   <span class="keyword">private</span> <span class="keyword">static</span>&lt;T extends Comparable&lt;T&gt;&gt; <span class="function"><span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(T[] x, <span class="keyword">int</span> low, <span class="keyword">int</span> high, T key)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">if</span>(low &lt;= high) &#123;</span><br><span class="line">        <span class="keyword">int</span> mid = low + ((high -low) &gt;&gt; <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">if</span>(key.compareTo(x[mid])== <span class="number">0</span>) &#123;</span><br><span class="line">           <span class="keyword">return</span> mid;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(key.compareTo(x[mid])&lt; <span class="number">0</span>) &#123;</span><br><span class="line">           <span class="keyword">return</span> binarySearch(x,low, mid - <span class="number">1</span>, key);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">           <span class="keyword">return</span> binarySearch(x,mid + <span class="number">1</span>, high, key);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>说明：</strong>上面的代码中给出了折半查找的两个版本，一个用递归实现，一个用循环实现。需要注意的是计算中间位置时不应该使用 (high+ low) / 2 的方式，因为加法运算可能导致整数越界，这里应该使用以下三种方式之一：low + (high - low) / 2 或 low + (high – low) &gt;&gt; 1 或 (low + high) &gt;&gt;&gt; 1（&gt;&gt;&gt; 是逻辑右移，是不带符号位的右移）</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Java/">Java</category>
      
      
      <comments>https://blog.eurkon.com/post/1cfbd3b3.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Java 面试题解析（反射）</title>
      <link>https://blog.eurkon.com/post/9fbf7373.html</link>
      <guid>https://blog.eurkon.com/post/9fbf7373.html</guid>
      <pubDate>Sun, 21 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;获得一个类的类对象有哪些方式？&quot;&gt;&lt;a href=&quot;#获得一个类的类对象有哪些方式？&quot; class=&quot;headerlink&quot; title=&quot;获得一个类的类对象有哪些方式？&quot;&gt;&lt;/a&gt;获得一个类的类对象有哪些方式？&lt;/h2&gt;&lt;p&gt;答：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;类型</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="获得一个类的类对象有哪些方式？"><a href="#获得一个类的类对象有哪些方式？" class="headerlink" title="获得一个类的类对象有哪些方式？"></a>获得一个类的类对象有哪些方式？</h2><p>答：</p><ul><li>类型.class，例如：<code>String.class</code></li><li>对象.getClass()，例如：<code>&quot;hello&quot;.getClass()</code></li><li>Class.forName()，例如：<code>Class.forName(&quot;java.lang.String&quot;)</code></li></ul><h2 id="如何通过反射创建对象？"><a href="#如何通过反射创建对象？" class="headerlink" title="如何通过反射创建对象？"></a>如何通过反射创建对象？</h2><p>答：</p><ul><li>通过类对象调用 <code>newInstance()</code> 方法，例如：<code>String.class.newInstance()</code></li><li>通过类对象的 <code>getConstructor()</code> 或 <code>getDeclaredConstructor()</code> 方法获得构造器（Constructor）对象并调用其 <code>newInstance()</code> 方法创建对象，例如：<code>String.class.getConstructor(String.class).newInstance(&quot;Hello&quot;);</code></li></ul><h2 id="如何通过反射获取和设置对象私有字段的值？"><a href="#如何通过反射获取和设置对象私有字段的值？" class="headerlink" title="如何通过反射获取和设置对象私有字段的值？"></a>如何通过反射获取和设置对象私有字段的值？</h2><p>答：可以通过类对象的 <code>getDeclaredField()</code> 方法字段（Field）对象，然后再通过字段对象的 <code>setAccessible(true)</code> 将其设置为可以访问，接下来就可以通过 get/set 方法来获取/设置字段的值了。下面的代码实现了一个反射的工具类，其中的两个静态方法分别用于获取和设置私有字段的值，字段可以是基本类型也可以是对象类型且支持多级对象操作，例如 <code>ReflectionUtil.get(dog, &quot;owner.car.engine.id&quot;);</code> 可以获得 dog 对象的主人的汽车的引擎的 ID 号。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.lang.reflect.Constructor;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Field;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Modifier;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 反射工具类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ReflectionUtil</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">ReflectionUtil</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> AssertionError();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通过反射取对象指定字段(属性)的值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> target 目标对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> fieldName 字段的名字</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> 如果取不到对象指定字段的值则抛出异常</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 字段的值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">getValue</span><span class="params">(Object target, String fieldName)</span> </span>&#123;</span><br><span class="line">        Class&lt;?&gt; clazz = target.getClass();</span><br><span class="line">        String[] fs = fieldName.split(<span class="string">&quot;\\.&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; fs.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">                Field f = clazz.getDeclaredField(fs[i]);</span><br><span class="line">                f.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">                target = f.get(target);</span><br><span class="line">                clazz = target.getClass();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            Field f = clazz.getDeclaredField(fs[fs.length - <span class="number">1</span>]);</span><br><span class="line">            f.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">            <span class="keyword">return</span> f.get(target);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通过反射给对象的指定字段赋值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> target 目标对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> fieldName 字段的名称</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">setValue</span><span class="params">(Object target, String fieldName, Object value)</span> </span>&#123;</span><br><span class="line">        Class&lt;?&gt; clazz = target.getClass();</span><br><span class="line">        String[] fs = fieldName.split(<span class="string">&quot;\\.&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; fs.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">                Field f = clazz.getDeclaredField(fs[i]);</span><br><span class="line">                f.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">                Object val = f.get(target);</span><br><span class="line">                <span class="keyword">if</span>(val == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    Constructor&lt;?&gt; c = f.getType().getDeclaredConstructor();</span><br><span class="line">                    c.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">                    val = c.newInstance();</span><br><span class="line">                    f.set(target, val);</span><br><span class="line">                &#125;</span><br><span class="line">                target = val;</span><br><span class="line">                clazz = target.getClass();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            Field f = clazz.getDeclaredField(fs[fs.length - <span class="number">1</span>]);</span><br><span class="line">            f.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">            f.set(target, value);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="如何通过反射调用对象的方法？"><a href="#如何通过反射调用对象的方法？" class="headerlink" title="如何通过反射调用对象的方法？"></a>如何通过反射调用对象的方法？</h2><p>答：请看下面的代码：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MethodInvokeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        String str = <span class="string">&quot;hello&quot;</span>;</span><br><span class="line">        Method m = str.getClass().getMethod(<span class="string">&quot;toUpperCase&quot;</span>);</span><br><span class="line">        System.out.println(m.invoke(str));  <span class="comment">// HELLO</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Java/">Java</category>
      
      
      <comments>https://blog.eurkon.com/post/9fbf7373.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Java 面试题解析（数据库）</title>
      <link>https://blog.eurkon.com/post/73adfcb0.html</link>
      <guid>https://blog.eurkon.com/post/73adfcb0.html</guid>
      <pubDate>Sat, 20 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;XML-文档定义有几种形式？它们之间有何本质区别？解析-XML-文档有哪几种方式？&quot;&gt;&lt;a href=&quot;#XML-文档定义有几种形式？它们之间有何本质区别？解析-XML-文档有哪几种方式？&quot; class=&quot;headerlink&quot; title=&quot;XML 文档定义有几</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="XML-文档定义有几种形式？它们之间有何本质区别？解析-XML-文档有哪几种方式？"><a href="#XML-文档定义有几种形式？它们之间有何本质区别？解析-XML-文档有哪几种方式？" class="headerlink" title="XML 文档定义有几种形式？它们之间有何本质区别？解析 XML 文档有哪几种方式？"></a>XML 文档定义有几种形式？它们之间有何本质区别？解析 XML 文档有哪几种方式？</h2><p>答：XML 文档定义分为 DTD 和 Schema 两种形式，二者都是对 XML 语法的约束，其本质区别在于 Schema 本身也是一个 XML 文件，可以被 XML 解析器解析，而且可以为 XML 承载的数据定义类型，约束能力较之 DTD 更强大。对 XML 的解析主要有 DOM（文档对象模型，Document Object Model）、SAX（Simple API for XML）和 StAX（Java 6 中引入的新的解析 XML 的方式，Streaming API for XML），其中 DOM 处理大型文件时其性能下降的非常厉害，这个问题是由 DOM 树结构占用的内存较多造成的，而且 DOM 解析方式必须在解析文件之前把整个文档装入内存，适合对 XML 的随机访问（典型的用空间换取时间的策略）；SAX 是事件驱动型的 XML 解析方式，它顺序读取 XML 文件，不需要一次全部装载整个文件。当遇到像文件开头，文档结束，或者标签开头与标签结束时，它会触发一个事件，用户通过事件回调代码来处理 XML 文件，适合对 XML 的顺序访问；顾名思义，StAX 把重点放在流上，实际上 StAX 与其他解析方式的本质区别就在于应用程序能够把 XML 作为一个事件流来处理。将 XML 作为一组事件来处理的想法并不新颖（SAX 就是这样做的），但不同之处在于 StAX 允许应用程序代码把这些事件逐个拉出来，而不用提供在解析器方便时从解析器中接收事件的处理程序。</p><h2 id="你在项目中哪些地方用到了-XML？"><a href="#你在项目中哪些地方用到了-XML？" class="headerlink" title="你在项目中哪些地方用到了 XML？"></a>你在项目中哪些地方用到了 XML？</h2><p>答：XML 的主要作用有两个方面：数据交换和信息配置。在做数据交换时，XML 将数据用标签组装成起来，然后压缩打包加密后通过网络传送给接收者，接收解密与解压缩后再从 XML 文件中还原相关信息进行处理，XML 曾经是异构系统间交换数据的事实标准，但此项功能几乎已经被 JSON（JavaScript Object Notation）取而代之。当然，目前很多软件仍然使用 XML 来存储配置信息，我们在很多项目中通常也会将作为配置信息的硬代码写在 XML 文件中，Java 的很多框架也是这么做的，而且这些框架都选择了 dom4j 作为处理 XML 的工具，因为 Sun 公司的官方 API 实在不怎么好用。</p><p><strong>补充：</strong>现在有很多时髦的软件（如 Sublime）已经开始将配置文件书写成 JSON 格式，我们已经强烈的感受到 XML 的另一项功能也将逐渐被业界抛弃。</p><h2 id="阐述-JDBC-操作数据库的步骤。"><a href="#阐述-JDBC-操作数据库的步骤。" class="headerlink" title="阐述 JDBC 操作数据库的步骤。"></a>阐述 JDBC 操作数据库的步骤。</h2><p>答：下面的代码以连接本机的 Oracle 数据库为例，演示 JDBC 操作数据库的步骤。</p><ol><li><p>加载驱动。</p><p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Class.forName(<span class="string">&quot;oracle.jdbc.driver.OracleDriver&quot;</span>);</span><br></pre></td></tr></table></figure></p></li><li><p>创建连接。</p><p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Connection con = DriverManager.getConnection(<span class="string">&quot;jdbc:oracle:thin:@localhost:1521:orcl&quot;</span>, <span class="string">&quot;scott&quot;</span>, <span class="string">&quot;tiger&quot;</span>);</span><br></pre></td></tr></table></figure></p></li><li><p>创建语句。</p><p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">PreparedStatement ps = con.prepareStatement(<span class="string">&quot;select * from emp where sal between ? and ?&quot;</span>);</span><br><span class="line">ps.setInt(<span class="number">1</span>, <span class="number">1000</span>);</span><br><span class="line">ps.setInt(<span class="number">2</span>, <span class="number">3000</span>);</span><br></pre></td></tr></table></figure></p></li><li><p>执行语句。</p><p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ResultSet rs = ps.executeQuery();</span><br></pre></td></tr></table></figure></p></li><li><p>处理结果。</p><p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(rs.next()) &#123;</span><br><span class="line">    System.out.println(rs.getInt(<span class="string">&quot;empno&quot;</span>) + <span class="string">&quot; - &quot;</span> + rs.getString(<span class="string">&quot;ename&quot;</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li><li><p>关闭资源。</p><p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(con != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            con.close();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li></ol><p><strong>注意：</strong>关闭外部资源的顺序应该和打开的顺序相反，也就是说先关闭 <code>ResultSet</code>、再关闭 <code>Statement</code>、最后关闭 <code>Connection</code>。上面的代码只关闭了 Connection`（连接），虽然通常情况下在关闭连接时，连接上创建的语句和打开的游标也会关闭，但不能保证总是如此，因此应该按照刚才说的顺序分别关闭。此外，第一步加载驱动在 JDBC 4.0 中是可以省略的（自动从类路径中加载驱动），但是我们建议保留。</p><h2 id="Statement-和-PreparedStatement-有什么区别？哪个性能更好？"><a href="#Statement-和-PreparedStatement-有什么区别？哪个性能更好？" class="headerlink" title="Statement 和 PreparedStatement 有什么区别？哪个性能更好？"></a><code>Statement</code> 和 <code>PreparedStatement</code> 有什么区别？哪个性能更好？</h2><p>答：与 <code>Statement</code> 相比，<code>PreparedStatement</code> 主要有以下优势：</p><ol><li><code>PreparedStatement</code> 接口代表预编译的语句，它主要的优势在于可以减少 SQL 的编译错误并增加 SQL 的安全性（减少 SQL 注射攻击的可能性）；</li><li><code>PreparedStatement</code> 中的 SQL 语句是可以带参数的，避免了用字符串连接拼接 SQL 语句的麻烦和不安全；</li><li>当批量处理 SQL 或频繁执行相同的查询时，<code>PreparedStatement</code>有明显的性能上的优势，由于数据库可以将编译优化后的 SQL 语句缓存起来，下次执行相同结构的语句时就会很快（不用再次编译和生成执行计划）。</li></ol><p><strong>补充：</strong>为了提供对存储过程的调用，JDBC API 中还提供了 <code>CallableStatement</code> 接口。存储过程（Stored Procedure）是数据库中一组为了完成特定功能的 SQL 语句的集合，经编译后存储在数据库中，用户通过指定存储过程的名字并给出参数（如果该存储过程带有参数）来执行它。虽然调用存储过程会在网络开销、安全性、性能上获得很多好处，但是存在如果底层数据库发生迁移时就会有很多麻烦，因为每种数据库的存储过程在书写上存在不少的差别。</p><h2 id="使用-JDBC-操作数据库时，如何提升读取数据的性能？如何提升更新数据的性能？"><a href="#使用-JDBC-操作数据库时，如何提升读取数据的性能？如何提升更新数据的性能？" class="headerlink" title="使用 JDBC 操作数据库时，如何提升读取数据的性能？如何提升更新数据的性能？"></a>使用 JDBC 操作数据库时，如何提升读取数据的性能？如何提升更新数据的性能？</h2><p>答：要<strong>提升读取数据的性能</strong>，可以指定通过结果集（ResultSet）对象的 <code>setFetchSize()</code> 方法指定每次抓取的记录数（典型的空间换时间策略）；要提升更新数据的性能可以使用 <code>PreparedStatement</code> 语句构建批处理，将若干 SQL 语句置于一个批处理中执行。</p><h2 id="在进行数据库编程时，连接池有什么作用？"><a href="#在进行数据库编程时，连接池有什么作用？" class="headerlink" title="在进行数据库编程时，连接池有什么作用？"></a>在进行数据库编程时，连接池有什么作用？</h2><p>答：由于创建连接和释放连接都有很大的开销（尤其是数据库服务器不在本地时，每次建立连接都需要进行 TCP 的三次握手，释放连接需要进行 TCP 四次握手，造成的开销是不可忽视的），为了提升系统访问数据库的性能，可以事先创建若干连接置于连接池中，需要时直接从连接池获取，使用结束时归还连接池而不必关闭连接，从而避免频繁创建和释放连接所造成的开销，这是典型的用空间换取时间的策略（浪费了空间存储连接，但节省了创建和释放连接的时间）。池化技术在 Java 开发中是很常见的，在使用线程时创建线程池的道理与此相同。基于 Java 的开源数据库连接池主要有：C3P0、Proxool、DBCP、BoneCP、Druid 等。</p><p><strong>补充：</strong>在计算机系统中时间和空间是不可调和的矛盾，理解这一点对设计满足性能要求的算法是至关重要的。大型网站性能优化的一个关键就是使用缓存，而缓存跟上面讲的连接池道理非常类似，也是使用空间换时间的策略。可以将热点数据置于缓存中，当用户查询这些数据时可以直接从缓存中得到，这无论如何也快过去数据库中查询。当然，缓存的置换策略等也会对系统性能产生重要影响，对于这个问题的讨论已经超出了这里要阐述的范围。</p><h2 id="什么是-DAO-模式？"><a href="#什么是-DAO-模式？" class="headerlink" title="什么是 DAO 模式？"></a>什么是 DAO 模式？</h2><p>答：DAO（Data Access Object）顾名思义是一个为数据库或其他持久化机制提供了抽象接口的对象，在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作。在实际的开发中，应该将所有对数据源的访问操作进行抽象化后封装在一个公共 API 中。用程序设计语言来说，就是建立一个接口，接口中定义了此应用程序中将会用到的所有事务方法。在这个应用程序中，当需要和数据源进行交互的时候则使用这个接口，并且编写一个单独的类来实现这个接口，在逻辑上该类对应一个特定的数据存储。DAO 模式实际上包含了两个模式，一是 Data Accessor（数据访问器），二是 Data Object（数据对象），前者要解决如何访问数据的问题，而后者要解决的是如何用对象封装数据。</p><h2 id="事务的-ACID-是指什么？"><a href="#事务的-ACID-是指什么？" class="headerlink" title="事务的 ACID 是指什么？"></a>事务的 ACID 是指什么？</h2><p>答：</p><ul><li>原子性（Atomic）：事务中各项操作，要么全做要么全不做，任何一项操作的失败都会导致整个事务的失败；</li><li>一致性（Consistent）：事务结束后系统状态是一致的；</li><li>隔离性（Isolated）：并发执行的事务彼此无法看到对方的中间状态；</li><li>持久性（Durable）：事务完成后所做的改动都会被持久化，即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。</li></ul><p><strong>补充：</strong>关于事务，在面试中被问到的概率是很高的，可以问的问题也是很多的。首先需要知道的是，只有存在<strong>并发数据访问</strong>时才需要事务。当多个事务访问同一数据时，可能会存在 5 类问题，包括 3 类<strong>数据读取</strong>问题（脏读、不可重复读和幻读）和 2 类<strong>数据更新</strong>问题（第 1 类丢失更新和第 2 类丢失更新）。</p><ul><li>脏读（Dirty Read）：A 事务读取 B 事务尚未提交的数据并在此基础上操作，而 B 事务执行回滚，那么 A 读取到的数据就是脏数据。</li></ul><div class="table-container"><table><thead><tr><th>时间</th><th>转账事务 A</th><th>取款事务 B</th></tr></thead><tbody><tr><td>T1</td><td>&nbsp;</td><td>开始事务</td></tr><tr><td>T2</td><td>开始事务</td><td>&nbsp;</td></tr><tr><td>T3</td><td>&nbsp;</td><td>查询账户余额为 1000 元</td></tr><tr><td>T4</td><td>&nbsp;</td><td>取出 500 元余额修改为 500 元</td></tr><tr><td>T5</td><td>查询账户余额为 500 元（脏读）</td><td>&nbsp;</td></tr><tr><td>T6</td><td>&nbsp;</td><td>撤销事务余额恢复为 1000 元</td></tr><tr><td>T7</td><td>汇入 100 元把余额修改为 600 元</td><td>&nbsp;</td></tr><tr><td>T8</td><td>提交事务</td><td>&nbsp;</td></tr></tbody></table></div><ul><li>不可重复读（Unrepeatable Read）：事务 A 重新读取前面读取过的数据，发现该数据已经被另一个已提交的事务 B 修改过了。</li></ul><div class="table-container"><table><thead><tr><th>时间</th><th>转账事务 A</th><th>取款事务 B</th></tr></thead><tbody><tr><td>T1</td><td>&nbsp;</td><td>开始事务</td></tr><tr><td>T2</td><td>开始事务</td><td>&nbsp;</td></tr><tr><td>T3</td><td>&nbsp;</td><td>查询账户余额为 1000 元</td></tr><tr><td>T4</td><td>查询账户余额为 1000 元</td><td>&nbsp;</td></tr><tr><td>T5</td><td>&nbsp;</td><td>取出 100 元修改余额为 900 元</td></tr><tr><td>T6</td><td>&nbsp;</td><td>提交事务</td></tr><tr><td>T7</td><td>查询账户余额为 900 元（不可重复读）</td><td>&nbsp;</td></tr></tbody></table></div><ul><li>幻读（Phantom Read）：事务 A 重新执行一个查询，返回一系列符合查询条件的行，发现其中插入了被事务 B 提交的行。</li></ul><div class="table-container"><table><thead><tr><th>时间</th><th>统计金额事务 A</th><th>转账事务 B</th></tr></thead><tbody><tr><td>T1</td><td>&nbsp;</td><td>开始事务</td></tr><tr><td>T2</td><td>开始事务</td><td>&nbsp;</td></tr><tr><td>T3</td><td>统计总存款为 10000 元</td><td>&nbsp;</td></tr><tr><td>T4</td><td>&nbsp;</td><td>新增一个存款账户存入 100 元</td></tr><tr><td>T5</td><td>&nbsp;</td><td>提交事务</td></tr><tr><td>T6</td><td>再次统计总存款为 10100 元（幻读）</td><td>&nbsp;</td></tr></tbody></table></div><ul><li>第 1 类丢失更新：事务 A 撤销时，把已经提交的事务 B 的更新数据覆盖了。</li></ul><div class="table-container"><table><thead><tr><th>时间</th><th>取款事务 A</th><th>转账事务 B</th></tr></thead><tbody><tr><td>T1</td><td>开始事务</td><td>&nbsp;</td></tr><tr><td>T2</td><td>&nbsp;</td><td>开始事务</td></tr><tr><td>T3</td><td>查询账户余额为 1000 元</td><td>&nbsp;</td></tr><tr><td>T4</td><td>&nbsp;</td><td>查询账户余额为 1000 元</td></tr><tr><td>T5</td><td>&nbsp;</td><td>汇入 100 元修改余额为 1100 元</td></tr><tr><td>T6</td><td>&nbsp;</td><td>提交事务</td></tr><tr><td>T7</td><td>取出 100 元将余额修改为 900 元</td><td>&nbsp;</td></tr><tr><td>T8</td><td>撤销事务</td><td>&nbsp;</td></tr><tr><td>T9</td><td>余额恢复为 1000 元（丢失更新）</td><td>&nbsp;</td></tr></tbody></table></div><ul><li>第 2 类丢失更新：事务 A 覆盖事务 B 已经提交的数据，造成事务 B 所做的操作丢失。</li></ul><div class="table-container"><table><thead><tr><th>时间</th><th>转账事务 A</th><th>取款事务 B</th></tr></thead><tbody><tr><td>T1</td><td>&nbsp;</td><td>开始事务</td></tr><tr><td>T2</td><td>开始事务</td><td>&nbsp;</td></tr><tr><td>T3</td><td>&nbsp;</td><td>查询账户余额为 1000 元</td></tr><tr><td>T4</td><td>查询账户余额为 1000 元</td><td>&nbsp;</td></tr><tr><td>T5</td><td>&nbsp;</td><td>取出 100 元将余额修改为 900 元</td></tr><tr><td>T6</td><td>&nbsp;</td><td>提交事务</td></tr><tr><td>T7</td><td>汇入 100 元将余额修改为 1100 元</td><td>&nbsp;</td></tr><tr><td>T8</td><td>提交事务</td><td>&nbsp;</td></tr><tr><td>T9</td><td>查询账户余额为 1100 元（丢失更新）</td><td>&nbsp;</td></tr></tbody></table></div><p>数据并发访问所产生的问题，在有些场景下可能是允许的，但是有些场景下可能就是致命的，数据库通常会通过<strong>锁机制</strong>来解决数据并发访问问题，按锁定对象不同可以分为<strong>表级锁</strong>和<strong>行级锁</strong>；按并发事务锁定关系可以分为<strong>共享锁</strong>和<strong>独占锁</strong>，具体的内容大家可以自行查阅资料进行了解。</p><p>直接使用锁是非常麻烦的，为此数据库为用户提供了自动锁机制，只要用户指定会话的事务隔离级别，数据库就会通过分析 SQL 语句然后为事务访问的资源加上合适的锁，此外，数据库还会维护这些锁通过各种手段提高系统的性能，这些对用户来说都是透明的（就是说你不用理解，事实上我确实也不知道）。ANSI/ISO SQL 92 标准定义了 4 个等级的事务隔离级别，如下表所示：</p><div class="table-container"><table><thead><tr><th>隔离级别</th><th>脏读</th><th>不可重复读</th><th>幻读</th><th>第一类丢失更新</th><th>第二类丢失更新</th></tr></thead><tbody><tr><td>READ UNCOMMITED</td><td>允许</td><td>允许</td><td>允许</td><td>不允许</td><td>允许</td></tr><tr><td>READ COMMITTED</td><td>不允许</td><td>允许</td><td>允许</td><td>不允许</td><td>允许</td></tr><tr><td>REPEATABLE READ</td><td>不允许</td><td>不允许</td><td>允许</td><td>不允许</td><td>不允许</td></tr><tr><td>SERIALIZABLE</td><td>不允许</td><td>不允许</td><td>不允许</td><td>不允许</td><td>不允许</td></tr></tbody></table></div><p><strong>注意：</strong>需要说明的是，事务隔离级别和数据访问的并发性是对立的，事务隔离级别越高并发性就越差。所以要根据具体的应用来确定合适的事务隔离级别，这个地方没有万能的原则。</p><h2 id="JDBC-中如何进行事务处理？"><a href="#JDBC-中如何进行事务处理？" class="headerlink" title="JDBC 中如何进行事务处理？"></a>JDBC 中如何进行事务处理？</h2><p>答：<code>Connection</code> 提供了事务处理的方法，通过调用 <code>setAutoCommit(false)</code> 可以设置手动提交事务；当事务完成后用 <code>commit()</code> 显式提交事务；如果在事务处理过程中发生异常则通过 <code>rollback()</code> 进行事务回滚。除此之外，从 JDBC 3.0 中还引入了 Savepoint（保存点）的概念，允许通过代码设置保存点并让事务回滚到指定的保存点。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/81.png" alt="JDBC 事务处理"></p><h2 id="JDBC-能否处理-Blob-和-Clob？"><a href="#JDBC-能否处理-Blob-和-Clob？" class="headerlink" title="JDBC 能否处理 Blob 和 Clob？"></a>JDBC 能否处理 Blob 和 Clob？</h2><p>答： Blob 是指二进制大对象（Binary Large Object），而 Clob 是指大字符对象（Character Large Objec），因此其中 Blob 是为存储大的二进制数据而设计的，而 Clob 是为存储大的文本数据而设计的。JDBC 的 <code>PreparedStatement</code> 和 <code>ResultSet</code> 都提供了相应的方法来支持 Blob 和 Clob 操作。下面的代码展示了如何使用 JDBC 操作 LOB：下面以 MySQL 数据库为例，创建一个张有三个字段的用户表，包括编号（<code>id</code>）、姓名（<code>name</code>）和照片（<code>photo</code>），建表语句如下：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> tb_user (</span><br><span class="line">  <span class="keyword">id</span> <span class="built_in">INT</span> PRIMARY <span class="keyword">KEY</span> auto_increment,</span><br><span class="line">  <span class="keyword">name</span> <span class="built_in">VARCHAR</span> ( <span class="number">20</span> ) <span class="keyword">UNIQUE</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">  photo LONGBLOB </span><br><span class="line">);</span><br></pre></td></tr></table></figure></p><p>下面的 Java 代码向数据库中插入一条记录：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.FileInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStream;</span><br><span class="line"><span class="keyword">import</span> java.sql.Connection;</span><br><span class="line"><span class="keyword">import</span> java.sql.DriverManager;</span><br><span class="line"><span class="keyword">import</span> java.sql.PreparedStatement;</span><br><span class="line"><span class="keyword">import</span> java.sql.SQLException;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">JdbcLobTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Connection con = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 1. 加载驱动（Java6 以上版本可以省略）</span></span><br><span class="line">            Class.forName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line">            <span class="comment">// 2. 建立连接</span></span><br><span class="line">            con = DriverManager.getConnection(<span class="string">&quot;jdbc:mysql://localhost:3306/test&quot;</span>, <span class="string">&quot;root&quot;</span>, <span class="string">&quot;123456&quot;</span>);</span><br><span class="line">            <span class="comment">// 3. 创建语句对象</span></span><br><span class="line">            PreparedStatement ps = con.prepareStatement(<span class="string">&quot;insert into tb_user values (default, ?, ?)&quot;</span>);</span><br><span class="line">            ps.setString(<span class="number">1</span>, <span class="string">&quot;Jack&quot;</span>);            <span class="comment">// 将 SQL 语句中第一个占位符换成字符串</span></span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> (InputStream in = <span class="keyword">new</span> FileInputStream(<span class="string">&quot;test.jpg&quot;</span>)) &#123;    <span class="comment">// Java 7 的 TWR</span></span><br><span class="line">                ps.setBinaryStream(<span class="number">2</span>, in);      <span class="comment">// 将 SQL 语句中第二个占位符换成二进制流</span></span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 4. SQL 语句获得受影响行数</span></span><br><span class="line">                System.out.println(ps.executeUpdate() == <span class="number">1</span> ? <span class="string">&quot;插入成功&quot;</span> : <span class="string">&quot;插入失败&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span>(IOException e) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;读取照片失败!&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException | SQLException e) &#123;     <span class="comment">// Java 7 的多异常捕获</span></span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;     <span class="comment">// 释放外部资源的代码都应当放在 finally 中保证其能够得到执行</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span>(con != <span class="keyword">null</span> &amp;&amp; !con.isClosed()) &#123;</span><br><span class="line">                    con.close();    <span class="comment">// 5. 释放数据库连接 </span></span><br><span class="line">                    con = <span class="keyword">null</span>;     <span class="comment">// 指示垃圾回收器可以回收该对象</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="简述正则表达式及其用途。"><a href="#简述正则表达式及其用途。" class="headerlink" title="简述正则表达式及其用途。"></a>简述正则表达式及其用途。</h2><p>答：在编写处理字符串的程序时，经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说，正则表达式就是记录文本规则的代码。</p><p><strong>说明：</strong>计算机诞生初期处理的信息几乎都是数值，但是时过境迁，今天我们使用计算机处理的信息更多的时候不是数值而是字符串，正则表达式就是在进行字符串匹配和处理的时候最为强大的工具，绝大多数语言都提供了对正则表达式的支持。</p><h2 id="Java-中是如何支持正则表达式操作的？"><a href="#Java-中是如何支持正则表达式操作的？" class="headerlink" title="Java 中是如何支持正则表达式操作的？"></a>Java 中是如何支持正则表达式操作的？</h2><p>答：Java 中的 <code>String</code> 类提供了支持正则表达式操作的方法，包括：<code>matches()</code>、<code>replaceAll()</code>、<code>replaceFirst()</code>、<code>split()</code>。此外，Java 中可以用 <code>Pattern</code> 类表示正则表达式对象，它提供了丰富的 API 进行各种正则表达式操作，请参考下面面试题的代码。</p><p>面试题：如果要从字符串中截取第一个英文左括号之前的字符串，例如：北京市(朝阳区)(西城区)(海淀区)，截取结果为：北京市，那么正则表达式怎么写？</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.regex.Matcher;</span><br><span class="line"><span class="keyword">import</span> java.util.regex.Pattern;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RegExpTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        String str = <span class="string">&quot;北京市(朝阳区)(西城区)(海淀区)&quot;</span>;</span><br><span class="line">        Pattern p = Pattern.compile(<span class="string">&quot;.*?(?=\\()&quot;</span>);</span><br><span class="line">        Matcher m = p.matcher(str);</span><br><span class="line">        <span class="keyword">if</span>(m.find()) &#123;</span><br><span class="line">            System.out.println(m.group());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>说明：</strong>上面的正则表达式中使用了懒惰匹配和前瞻，如果不清楚这些内容，推荐读一下网上很有名的<a href="https://www.jb51.net/tools/zhengze.html">《正则表达式 30 分钟入门教程》</a>。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Java/">Java</category>
      
      
      <comments>https://blog.eurkon.com/post/73adfcb0.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Java 面试题解析（IO流）</title>
      <link>https://blog.eurkon.com/post/af128a42.html</link>
      <guid>https://blog.eurkon.com/post/af128a42.html</guid>
      <pubDate>Fri, 19 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Java-中如何实现序列化，有什么意义？&quot;&gt;&lt;a href=&quot;#Java-中如何实现序列化，有什么意义？&quot; class=&quot;headerlink&quot; title=&quot;Java 中如何实现序列化，有什么意义？&quot;&gt;&lt;/a&gt;Java 中如何实现序列化，有什么意义？&lt;/h2&gt;&lt;</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Java-中如何实现序列化，有什么意义？"><a href="#Java-中如何实现序列化，有什么意义？" class="headerlink" title="Java 中如何实现序列化，有什么意义？"></a>Java 中如何实现序列化，有什么意义？</h2><p>答：序列化就是一种用来处理对象流的机制，所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作，也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操作时可能引发的问题（如果不进行序列化可能会存在数据乱序的问题）。</p><p>要实现序列化，需要让一个类实现 <code>Serializable</code> 接口，该接口是一个标识性接口，标注该类对象是可被序列化的，然后使用一个输出流来构造一个对象输出流并通过 <code>writeObject(Object)</code> 方法就可以将实现对象写出（即保存其状态）；如果需要反序列化则可以用一个输入流建立对象输入流，然后通过 <code>readObject</code> 方法从流中读取对象。序列化除了能够实现对象的持久化之外，还能够用于对象的深度克隆（可以参考第 29 题）。</p><h2 id="Java-中有几种类型的流？"><a href="#Java-中有几种类型的流？" class="headerlink" title="Java 中有几种类型的流？"></a>Java 中有几种类型的流？</h2><p>答：<strong>字节流</strong>和<strong>字符流</strong>。字节流继承于 <code>InputStream</code>、<code>OutputStream</code>，字符流继承于 <code>Reader</code>、<code>Writer</code>。在 <code>java.io</code> 包中还有许多其他的流，主要是为了提高性能和使用方便。关于 Java 的 I/O 需要注意的有两点：一是两种<strong>对称性</strong>（输入和输出的对称性，字节和字符的对称性）；二是两种<strong>设计模式</strong>（适配器模式和装潢模式）。另外 Java 中的流不同于 C# 的是它只有一个维度一个方向。</p><p>面试题 - 编程实现文件拷贝。（这个题目在笔试的时候经常出现，下面的代码给出了两种实现方案）</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.FileInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.FileOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.OutputStream;</span><br><span class="line"><span class="keyword">import</span> java.nio.ByteBuffer;</span><br><span class="line"><span class="keyword">import</span> java.nio.channels.FileChannel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">MyUtil</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 工具类中的方法都是静态方式访问的，因此将构造器私有不允许创建对象(绝对好习惯)</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">MyUtil</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> AssertionError();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">fileCopy</span><span class="params">(String source, String target)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> (InputStream in = <span class="keyword">new</span> FileInputStream(source)) &#123;</span><br><span class="line">            <span class="keyword">try</span> (OutputStream out = <span class="keyword">new</span> FileOutputStream(target)) &#123;</span><br><span class="line">                <span class="keyword">byte</span>[] buffer = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">4096</span>];</span><br><span class="line">                <span class="keyword">int</span> bytesToRead;</span><br><span class="line">                <span class="keyword">while</span>((bytesToRead = in.read(buffer)) != -<span class="number">1</span>) &#123;</span><br><span class="line">                    out.write(buffer, <span class="number">0</span>, bytesToRead);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">fileCopyNIO</span><span class="params">(String source, String target)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> (FileInputStream in = <span class="keyword">new</span> FileInputStream(source)) &#123;</span><br><span class="line">            <span class="keyword">try</span> (FileOutputStream out = <span class="keyword">new</span> FileOutputStream(target)) &#123;</span><br><span class="line">                FileChannel inChannel = in.getChannel();</span><br><span class="line">                FileChannel outChannel = out.getChannel();</span><br><span class="line">                ByteBuffer buffer = ByteBuffer.allocate(<span class="number">4096</span>);</span><br><span class="line">                <span class="keyword">while</span>(inChannel.read(buffer) != -<span class="number">1</span>) &#123;</span><br><span class="line">                    buffer.flip();</span><br><span class="line">                    outChannel.write(buffer);</span><br><span class="line">                    buffer.clear();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>上面用到 Java 7 的 TWR，使用 TWR 后可以不用在 <code>finally</code> 中释放外部资源 ，从而让代码更加优雅。</p><h2 id="写一个方法，输入一个文件名和一个字符串，统计这个字符串在这个文件中出现的次数。"><a href="#写一个方法，输入一个文件名和一个字符串，统计这个字符串在这个文件中出现的次数。" class="headerlink" title="写一个方法，输入一个文件名和一个字符串，统计这个字符串在这个文件中出现的次数。"></a>写一个方法，输入一个文件名和一个字符串，统计这个字符串在这个文件中出现的次数。</h2><p>答：代码如下：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.BufferedReader;</span><br><span class="line"><span class="keyword">import</span> java.io.FileReader;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">MyUtil</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 工具类中的方法都是静态方式访问的，因此将构造器私有不允许创建对象(绝对好习惯)</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">MyUtil</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> AssertionError();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 统计给定文件中给定字符串的出现次数</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> filename 文件名</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> word 字符串</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 字符串在文件中出现的次数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">countWordInFile</span><span class="params">(String filename, String word)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> counter = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">try</span> (FileReader fr = <span class="keyword">new</span> FileReader(filename)) &#123;</span><br><span class="line">            <span class="keyword">try</span> (BufferedReader br = <span class="keyword">new</span> BufferedReader(fr)) &#123;</span><br><span class="line">                String line = <span class="keyword">null</span>;</span><br><span class="line">                <span class="keyword">while</span> ((line = br.readLine()) != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">int</span> index = -<span class="number">1</span>;</span><br><span class="line">                    <span class="keyword">while</span> (line.length() &gt;= word.length() &amp;&amp; (index = line.indexOf(word)) &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                        counter++;</span><br><span class="line">                        line = line.substring(index + word.length());</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">            ex.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> counter;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="如何用-Java-代码列出一个目录下所有的文件？"><a href="#如何用-Java-代码列出一个目录下所有的文件？" class="headerlink" title="如何用 Java 代码列出一个目录下所有的文件？"></a>如何用 Java 代码列出一个目录下所有的文件？</h2><p>答：</p><p>如果只要求列出当前文件夹下的文件，代码如下所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test12</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        File f = <span class="keyword">new</span> File(<span class="string">&quot;/Users/Hao/Downloads&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span>(File temp : f.listFiles()) &#123;</span><br><span class="line">            <span class="keyword">if</span>(temp.isFile()) &#123;</span><br><span class="line">                System.out.println(temp.getName());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>如果需要对文件夹继续展开，代码如下所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test12</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        showDirectory(<span class="keyword">new</span> File(<span class="string">&quot;/Users/Hao/Downloads&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">showDirectory</span><span class="params">(File f)</span> </span>&#123;</span><br><span class="line">        _walkDirectory(f, <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">_walkDirectory</span><span class="params">(File f, <span class="keyword">int</span> level)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(f.isDirectory()) &#123;</span><br><span class="line">            <span class="keyword">for</span>(File temp : f.listFiles()) &#123;</span><br><span class="line">                _walkDirectory(temp, level + <span class="number">1</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; level - <span class="number">1</span>; i++) &#123;</span><br><span class="line">                System.out.print(<span class="string">&quot;\t&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(f.getName());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在 Java 7 中可以使用 NIO.2 的 API 来做同样的事情，代码如下所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ShowFileTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        Path initPath = Paths.get(<span class="string">&quot;/Users/Hao/Downloads&quot;</span>);</span><br><span class="line">        Files.walkFileTree(initPath, <span class="keyword">new</span> SimpleFileVisitor&lt;Path&gt;() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> FileVisitResult <span class="title">visitFile</span><span class="params">(Path file, BasicFileAttributes attrs)</span> </span></span><br><span class="line"><span class="function">                    <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">                System.out.println(file.getFileName().toString());</span><br><span class="line">                <span class="keyword">return</span> FileVisitResult.CONTINUE;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="用-Java-的套接字编程实现一个多线程的回显（echo）服务器。"><a href="#用-Java-的套接字编程实现一个多线程的回显（echo）服务器。" class="headerlink" title="用 Java 的套接字编程实现一个多线程的回显（echo）服务器。"></a>用 Java 的套接字编程实现一个多线程的回显（echo）服务器。</h2><p>答：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.BufferedReader;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStreamReader;</span><br><span class="line"><span class="keyword">import</span> java.io.PrintWriter;</span><br><span class="line"><span class="keyword">import</span> java.net.ServerSocket;</span><br><span class="line"><span class="keyword">import</span> java.net.Socket;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EchoServer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ECHO_SERVER_PORT = <span class="number">6789</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;        </span><br><span class="line">        <span class="keyword">try</span>(ServerSocket server = <span class="keyword">new</span> ServerSocket(ECHO_SERVER_PORT)) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;服务器已经启动...&quot;</span>);</span><br><span class="line">            <span class="keyword">while</span>(<span class="keyword">true</span>) &#123;</span><br><span class="line">                Socket client = server.accept();</span><br><span class="line">                <span class="keyword">new</span> Thread(<span class="keyword">new</span> ClientHandler(client)).start();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ClientHandler</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> Socket client;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">ClientHandler</span><span class="params">(Socket client)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.client = client;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">try</span>(BufferedReader br = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(client.getInputStream()));</span><br><span class="line">                    PrintWriter pw = <span class="keyword">new</span> PrintWriter(client.getOutputStream())) &#123;</span><br><span class="line">                String msg = br.readLine();</span><br><span class="line">                System.out.println(<span class="string">&quot;收到&quot;</span> + client.getInetAddress() + <span class="string">&quot;发送的: &quot;</span> + msg);</span><br><span class="line">                pw.println(msg);</span><br><span class="line">                pw.flush();</span><br><span class="line">            &#125; <span class="keyword">catch</span>(Exception ex) &#123;</span><br><span class="line">                ex.printStackTrace();</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    client.close();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>上面的代码使用了 Java 7 的 TWR 语法，由于很多外部资源类都间接的实现了 <code>AutoCloseable</code> 接口（单方法回调接口），因此可以利用 TWR 语法在 <code>try</code> 结束的时候通过回调的方式自动调用外部资源类的 <code>close()</code> 方法，避免书写冗长的 <code>finally</code> 代码块。此外，上面的代码用一个静态内部类实现线程的功能，使用多线程可以避免一个用户 I/O 操作所产生的中断影响其他用户对服务器的访问，简单的说就是一个用户的输入操作不会造成其他用户的阻塞。当然，上面的代码使用线程池可以获得更好的性能，因为频繁的创建和销毁线程所造成的开销也是不可忽视的。</p><p>下面是一段回显客户端测试代码：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.BufferedReader;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStreamReader;</span><br><span class="line"><span class="keyword">import</span> java.io.PrintWriter;</span><br><span class="line"><span class="keyword">import</span> java.net.Socket;</span><br><span class="line"><span class="keyword">import</span> java.util.Scanner;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EchoClient</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        Socket client = <span class="keyword">new</span> Socket(<span class="string">&quot;localhost&quot;</span>, <span class="number">6789</span>);</span><br><span class="line">        Scanner sc = <span class="keyword">new</span> Scanner(System.in);</span><br><span class="line">        System.out.print(<span class="string">&quot;请输入内容: &quot;</span>);</span><br><span class="line">        String msg = sc.nextLine();</span><br><span class="line">        sc.close();</span><br><span class="line">        PrintWriter pw = <span class="keyword">new</span> PrintWriter(client.getOutputStream());</span><br><span class="line">        pw.println(msg);</span><br><span class="line">        pw.flush();</span><br><span class="line">        BufferedReader br = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(client.getInputStream()));</span><br><span class="line">        System.out.println(br.readLine());</span><br><span class="line">        client.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>如果希望用 NIO 的多路复用套接字实现服务器，代码如下所示。NIO 的操作虽然带来了更好的性能，但是有些操作是比较底层的，对于初学者来说还是有些难于理解。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.net.InetSocketAddress;</span><br><span class="line"><span class="keyword">import</span> java.nio.ByteBuffer;</span><br><span class="line"><span class="keyword">import</span> java.nio.CharBuffer;</span><br><span class="line"><span class="keyword">import</span> java.nio.channels.SelectionKey;</span><br><span class="line"><span class="keyword">import</span> java.nio.channels.Selector;</span><br><span class="line"><span class="keyword">import</span> java.nio.channels.ServerSocketChannel;</span><br><span class="line"><span class="keyword">import</span> java.nio.channels.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> java.util.Iterator;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EchoServerNIO</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ECHO_SERVER_PORT = <span class="number">6789</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ECHO_SERVER_TIMEOUT = <span class="number">5000</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> BUFFER_SIZE = <span class="number">1024</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> ServerSocketChannel serverChannel = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Selector selector = <span class="keyword">null</span>;    <span class="comment">// 多路复用选择器</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> ByteBuffer buffer = <span class="keyword">null</span>;    <span class="comment">// 缓冲区</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        init();</span><br><span class="line">        listen();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            serverChannel = ServerSocketChannel.open();</span><br><span class="line">            buffer = ByteBuffer.allocate(BUFFER_SIZE);</span><br><span class="line">            serverChannel.socket().bind(<span class="keyword">new</span> InetSocketAddress(ECHO_SERVER_PORT));</span><br><span class="line">            serverChannel.configureBlocking(<span class="keyword">false</span>);</span><br><span class="line">            selector = Selector.open();</span><br><span class="line">            serverChannel.register(selector, SelectionKey.OP_ACCEPT);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">listen</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (selector.select(ECHO_SERVER_TIMEOUT) != <span class="number">0</span>) &#123;</span><br><span class="line">                    Iterator&lt;SelectionKey&gt; it = selector.selectedKeys().iterator();</span><br><span class="line">                    <span class="keyword">while</span> (it.hasNext()) &#123;</span><br><span class="line">                        SelectionKey key = it.next();</span><br><span class="line">                        it.remove();</span><br><span class="line">                        handleKey(key);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">handleKey</span><span class="params">(SelectionKey key)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        SocketChannel channel = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (key.isAcceptable()) &#123;</span><br><span class="line">                ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();</span><br><span class="line">                channel = serverChannel.accept();</span><br><span class="line">                channel.configureBlocking(<span class="keyword">false</span>);</span><br><span class="line">                channel.register(selector, SelectionKey.OP_READ);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (key.isReadable()) &#123;</span><br><span class="line">                channel = (SocketChannel) key.channel();</span><br><span class="line">                buffer.clear();</span><br><span class="line">                <span class="keyword">if</span> (channel.read(buffer) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    buffer.flip();</span><br><span class="line">                    CharBuffer charBuffer = CharsetHelper.decode(buffer);</span><br><span class="line">                    String msg = charBuffer.toString();</span><br><span class="line">                    System.out.println(<span class="string">&quot;收到&quot;</span> + channel.getRemoteAddress() + <span class="string">&quot;的消息：&quot;</span> + msg);</span><br><span class="line">                    channel.write(CharsetHelper.encode(CharBuffer.wrap(msg)));</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    channel.close();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">if</span> (channel != <span class="keyword">null</span>) &#123;</span><br><span class="line">                channel.close();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.nio.ByteBuffer;</span><br><span class="line"><span class="keyword">import</span> java.nio.CharBuffer;</span><br><span class="line"><span class="keyword">import</span> java.nio.charset.CharacterCodingException;</span><br><span class="line"><span class="keyword">import</span> java.nio.charset.Charset;</span><br><span class="line"><span class="keyword">import</span> java.nio.charset.CharsetDecoder;</span><br><span class="line"><span class="keyword">import</span> java.nio.charset.CharsetEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">CharsetHelper</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String UTF_8 = <span class="string">&quot;UTF-8&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> CharsetEncoder encoder = Charset.forName(UTF_8).newEncoder();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> CharsetDecoder decoder = Charset.forName(UTF_8).newDecoder();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">CharsetHelper</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ByteBuffer <span class="title">encode</span><span class="params">(CharBuffer in)</span> <span class="keyword">throws</span> CharacterCodingException</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> encoder.encode(in);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> CharBuffer <span class="title">decode</span><span class="params">(ByteBuffer in)</span> <span class="keyword">throws</span> CharacterCodingException</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> decoder.decode(in);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Java/">Java</category>
      
      
      <comments>https://blog.eurkon.com/post/af128a42.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Java 面试题解析（并发）</title>
      <link>https://blog.eurkon.com/post/32cf50db.html</link>
      <guid>https://blog.eurkon.com/post/32cf50db.html</guid>
      <pubDate>Thu, 18 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Thread-类的-sleep-方法和对象的-wait-方法都可以让线程暂停执行，它们有什么区别&quot;&gt;&lt;a href=&quot;#Thread-类的-sleep-方法和对象的-wait-方法都可以让线程暂停执行，它们有什么区别&quot; class=&quot;headerlink&quot; tit</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Thread-类的-sleep-方法和对象的-wait-方法都可以让线程暂停执行，它们有什么区别"><a href="#Thread-类的-sleep-方法和对象的-wait-方法都可以让线程暂停执行，它们有什么区别" class="headerlink" title="Thread 类的 sleep() 方法和对象的 wait() 方法都可以让线程暂停执行，它们有什么区别?"></a><code>Thread</code> 类的 <code>sleep()</code> 方法和对象的 <code>wait()</code> 方法都可以让线程暂停执行，它们有什么区别?</h2><p>答：<code>sleep()</code> 方法（休眠）是线程类 <code>Thread</code> 的静态方法，调用此方法会让当前线程暂停执行指定的时间，将执行机会（CPU）让给其他线程，但是对象的锁依然保持，因此休眠时间结束后会自动恢复（线程回到就绪状态，请参考第 66 题中的线程状态转换图）。<code>wait()</code> 是 <code>Object</code> 类的方法，调用对象的 <code>wait()</code> 方法导致当前线程放弃对象的锁（线程暂停执行），进入对象的等待池（wait pool），只有调用对象的 <code>notify()</code> 方法（或 <code>notifyAll()</code> 方法）时才能唤醒等待池中的线程进入等锁池（lock pool），如果线程重新获得对象的锁就可以进入就绪状态。</p><p><strong>补充：</strong>可能不少人对什么是进程，什么是线程还比较模糊，对于为什么需要多线程编程也不是特别理解。简单的说：进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动，是操作系统进行资源分配和调度的一个独立单位；线程是进程的一个实体，是 CPU 调度和分派的基本单位，是比进程更小的能独立运行的基本单位。线程的划分尺度小于进程，这使得多线程程序的并发性高；进程在执行时通常拥有独立的内存单元，而线程之间可以共享内存。使用多线程的编程通常能够带来更好的性能和用户体验，但是多线程的程序对于其他程序是不友好的，因为它可能占用了更多的 CPU 资源。当然，也不是线程越多，程序的性能就越好，因为线程之间的调度和切换也会浪费 CPU 时间。时下很时髦的 Node.js 就采用了单线程异步 I/O 的工作模式。</p><h2 id="线程的-sleep-方法和-yield-方法有什么区别？"><a href="#线程的-sleep-方法和-yield-方法有什么区别？" class="headerlink" title="线程的 sleep() 方法和 yield() 方法有什么区别？"></a>线程的 <code>sleep()</code> 方法和 <code>yield()</code> 方法有什么区别？</h2><p>答：</p><ol><li><code>sleep()</code> 方法给其他线程运行机会时不考虑线程的优先级，因此会给低优先级的线程以运行的机会；<code>yield()</code> 方法只会给相同优先级或更高优先级的线程以运行的机会；</li><li>线程执行 <code>sleep()</code> 方法后转入阻塞（blocked）状态，而执行 <code>yield()</code> 方法后转入就绪（ready）状态；</li><li><code>sleep()</code> 方法声明抛出 <code>InterruptedException</code>，而 <code>yield()</code> 方法没有声明任何异常；</li><li><code>sleep()</code> 方法比 <code>yield()</code> 方法（跟操作系统 CPU 调度相关）具有更好的可移植性。</li></ol><h2 id="当一个线程进入一个对象的-synchronized-方法-A-之后，其它线程是否可进入此对象的-synchronized-方法-B？"><a href="#当一个线程进入一个对象的-synchronized-方法-A-之后，其它线程是否可进入此对象的-synchronized-方法-B？" class="headerlink" title="当一个线程进入一个对象的 synchronized 方法 A 之后，其它线程是否可进入此对象的 synchronized 方法 B？"></a>当一个线程进入一个对象的 <code>synchronized</code> 方法 <code>A</code> 之后，其它线程是否可进入此对象的 <code>synchronized</code> 方法 <code>B</code>？</h2><p>答：不能。其它线程只能访问该对象的非同步方法，同步方法则不能进入。因为非静态方法上的 <code>synchronized</code> 修饰符要求执行方法时要获得对象的锁，如果已经进入 <code>A</code> 方法说明对象锁已经被取走，那么试图进入 <code>B</code> 方法的线程就只能在<strong>等锁池</strong>（注意不是等待池哦）中等待对象的锁。</p><h2 id="请说出与线程同步以及线程调度相关的方法。"><a href="#请说出与线程同步以及线程调度相关的方法。" class="headerlink" title="请说出与线程同步以及线程调度相关的方法。"></a>请说出与线程同步以及线程调度相关的方法。</h2><p>答：</p><ul><li><code>wait()</code>：使一个线程处于等待（阻塞）状态，并且释放所持有的对象的锁；</li><li><code>sleep()</code>：使一个正在运行的线程处于睡眠状态，是一个静态方法，调用此方法要处理 <code>InterruptedException</code> 异常；</li><li><code>notify()</code>：唤醒一个处于等待状态的线程，当然在调用此方法的时候，并不能确切的唤醒某一个等待状态的线程，而是由 JVM 确定唤醒哪个线程，而且<strong>与优先级无关</strong>；</li><li><code>notityAll()</code>：唤醒所有处于等待状态的线程，该方法并不是将对象的锁给所有线程，而是让它们<strong>竞争</strong>，只有获得锁的线程才能进入就绪状态；</li></ul><p><strong>说明：</strong>关于 Java 多线程和并发编程的问题，建议大家看我的另一篇文章<a href="https://blog.csdn.net/jackfrued/article/details/44499227">《关于 Java 并发编程的总结和思考》</a>。</p><p><strong>补充：</strong>Java 5 通过 <code>Lock</code> 接口提供了显式的锁机制（explicit lock），增强了灵活性以及对线程的协调。<code>Lock</code> 接口中定义了加锁 <code>lock()</code> 和解锁 <code>unlock()</code> 的方法，同时还提供了 <code>newCondition()</code> 方法来产生用于线程之间通信的 <code>Condition</code> 对象；此外，Java 5 还提供了信号量机制（semaphore），信号量可以用来限制对某个共享资源进行访问的线程的数量。在对资源进行访问之前，线程必须得到信号量的许可（调用 <code>Semaphore</code> 对象的 <code>acquire()</code> 方法）；在完成对资源的访问后，线程必须向信号量归还许可（调用 <code>Semaphore</code> 对象的 <code>release()</code> 方法）。</p><p>下面的例子演示了 100 个线程同时向一个银行账户中存入 1 元钱，在没有使用同步机制和使用同步机制情况下的执行情况。</p><p>银行账户类：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 银行账户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">double</span> balance;       <span class="comment">// 账户余额</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 存款</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> money 存入金额</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">deposit</span><span class="params">(<span class="keyword">double</span> money)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">double</span> newBalance = balance + money;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">10</span>);     <span class="comment">// 模拟此业务需要一段处理时间</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span>(InterruptedException ex) &#123;</span><br><span class="line">            ex.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        balance = newBalance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获得账户余额</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">getBalance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> balance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 存钱线程类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AddMoneyThread</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Account account;    <span class="comment">// 存入账户</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">double</span> money;       <span class="comment">// 存入金额</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AddMoneyThread</span><span class="params">(Account account, <span class="keyword">double</span> money)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.account = account;</span><br><span class="line">        <span class="keyword">this</span>.money = money;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        account.deposit(money);   <span class="comment">// 调用存款方法</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.concurrent.ExecutorService;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Executors;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 测试类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test01</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Account account = <span class="keyword">new</span> Account();</span><br><span class="line">        ExecutorService service = Executors.newFixedThreadPool(<span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">100</span>; i++) &#123;</span><br><span class="line">            service.execute(<span class="keyword">new</span> AddMoneyThread(account, <span class="number">1</span>));</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        service.shutdown();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span>(!service.isTerminated()) &#123;&#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;账户余额: &quot;</span> + account.getBalance());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在没有同步的情况下，执行结果通常是显示账户余额在 10 元以下，出现这种状况的原因是，当一个线程 A 试图存入 1 元的时候，另外一个线程 B 也能够进入存款的方法中，线程 B 读取到的账户余额仍然是线程 A 存入 1 元钱之前的账户余额，因此也是在原来的余额 0 上面做了加 1 元的操作，同理线程 C 也会做类似的事情，所以最后 100 个线程执行结束时，本来期望账户余额为 100 元，但实际得到的通常在 10 元以下（很可能是 1 元哦）。解决这个问题的办法就是同步，当一个线程对银行账户存钱时，需要将此账户锁定，待其操作完成后才允许其他的线程进行操作，代码有如下几种调整方案：</p><ol><li><p>在银行账户的存款（<code>deposit</code>）方法上同步（<code>synchronized</code>）关键字</p><p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 银行账户</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">double</span> balance;     <span class="comment">// 账户余额</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 存款</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> money 存入金额</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">deposit</span><span class="params">(<span class="keyword">double</span> money)</span> </span>&#123;  <span class="comment">// 加上同步（synchronized）关键字</span></span><br><span class="line">        <span class="keyword">double</span> newBalance = balance + money;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">10</span>);   <span class="comment">// 模拟此业务需要一段处理时间</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span>(InterruptedException ex) &#123;</span><br><span class="line">            ex.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        balance = newBalance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 获得账户余额</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">getBalance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> balance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li><li><p>在线程调用存款方法时对银行账户进行同步</p><p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 存钱线程</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AddMoneyThread</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Account account;    <span class="comment">// 存入账户</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">double</span> money;       <span class="comment">// 存入金额</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AddMoneyThread</span><span class="params">(Account account, <span class="keyword">double</span> money)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.account = account;</span><br><span class="line">        <span class="keyword">this</span>.money = money;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (account) &#123;      <span class="comment">// 加上同步（synchronized）关键字</span></span><br><span class="line">            account.deposit(money);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li><li><p>通过 Java 5 显示的锁机制，为每个银行账户创建一个锁对象，在存款操作进行加锁和解锁的操作</p><p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.Lock;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.ReentrantLock;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 银行账户</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Lock accountLock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">double</span> balance; <span class="comment">// 账户余额</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 存入金额</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> money</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">deposit</span><span class="params">(<span class="keyword">double</span> money)</span> </span>&#123;</span><br><span class="line">        accountLock.lock(); <span class="comment">// 加锁</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">double</span> newBalance = balance + money;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(<span class="number">10</span>); <span class="comment">// 模拟此业务需要一段处理时间</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> (InterruptedException ex) &#123;</span><br><span class="line">                ex.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            balance = newBalance;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">finally</span> &#123;</span><br><span class="line">            accountLock.unlock(); <span class="comment">// 解锁</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 获得账户余额</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">getBalance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> balance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p></li></ol><p>按照上述三种方式对代码进行修改后，重写执行测试代码 Test01，将看到最终的账户余额为 100 元。当然也可以使用 <code>Semaphore</code> 或 <code>CountdownLatch</code> 来实现同步。</p><h2 id="编写多线程程序有几种实现方式？"><a href="#编写多线程程序有几种实现方式？" class="headerlink" title="编写多线程程序有几种实现方式？"></a>编写多线程程序有几种实现方式？</h2><p>答：Java 5 以前实现多线程有两种实现方法：一种是继承 <code>Thread</code> 类；另一种是实现 <code>Runnable</code> 接口。两种方式都要通过重写 <code>run()</code> 方法来定义线程的行为，推荐使用后者，因为 Java 中的继承是单继承，一个类有一个父类，如果继承了 <code>Thread</code> 类就无法再继承其他类了，显然使用 <code>Runnable</code> 接口更为灵活。</p><p><strong>补充：</strong>Java 5 以后创建线程还有第三种方式：实现 <code>Callable</code> 接口，该接口中的 <code>call</code> 方法可以在线程执行结束时产生一个返回值，代码如下所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Callable;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.ExecutorService;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Executors;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Future;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyTask</span> <span class="keyword">implements</span> <span class="title">Callable</span>&lt;<span class="title">Integer</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> upperBounds;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyTask</span><span class="params">(<span class="keyword">int</span> upperBounds)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.upperBounds = upperBounds;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Integer <span class="title">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> sum = <span class="number">0</span>; </span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= upperBounds; i++) &#123;</span><br><span class="line">            sum += i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sum;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        List&lt;Future&lt;Integer&gt;&gt; list = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">        ExecutorService service = Executors.newFixedThreadPool(<span class="number">10</span>);</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            list.add(service.submit(<span class="keyword">new</span> MyTask((<span class="keyword">int</span>) (Math.random() * <span class="number">100</span>))));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> sum = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(Future&lt;Integer&gt; future : list) &#123;</span><br><span class="line">            <span class="comment">// while(!future.isDone()) ;</span></span><br><span class="line">            sum += future.get();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(sum);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="synchronized-关键字的用法？"><a href="#synchronized-关键字的用法？" class="headerlink" title="synchronized 关键字的用法？"></a><code>synchronized</code> 关键字的用法？</h2><p>答：<code>synchronized</code> 关键字可以将对象或者方法标记为<strong>同步</strong>，以实现对对象和方法的互斥访问，可以用 <code>synchronized(对象) &#123; … &#125;</code> 定义同步代码块，或者在声明方法时将 <code>synchronized</code> 作为方法的修饰符。在第 60 题的例子中已经展示了 <code>synchronized</code> 关键字的用法。</p><h2 id="举例说明同步和异步。"><a href="#举例说明同步和异步。" class="headerlink" title="举例说明同步和异步。"></a>举例说明同步和异步。</h2><p>答：如果系统中存在临界资源（资源数量少于竞争资源的线程数量的资源），例如正在写的数据以后可能被另一个线程读到，或者正在读的数据可能已经被另一个线程写过了，那么这些数据就必须进行同步存取（数据库操作中的排他锁就是最好的例子）。当应用程序在对象上调用了一个需要花费很长时间来执行的方法，并且不希望让程序等待方法的返回时，就应该使用异步编程，在很多情况下采用异步途径往往更有效率。事实上，所谓的同步就是指<strong>阻塞式操作</strong>，而异步就是<strong>非阻塞式操作</strong>。</p><h2 id="启动一个线程是调用-run-还是-start-方法？"><a href="#启动一个线程是调用-run-还是-start-方法？" class="headerlink" title="启动一个线程是调用 run() 还是 start() 方法？"></a>启动一个线程是调用 <code>run()</code> 还是 <code>start()</code> 方法？</h2><p>答：启动一个线程是调用 <code>start()</code> 方法，使线程所代表的虚拟处理机处于可运行状态，这意味着它可以由 JVM 调度并执行，这并不意味着线程就会立即运行。<code>run()</code> 方法是线程启动后要进行回调（callback）的方法。</p><h2 id="什么是线程池（thread-pool）？"><a href="#什么是线程池（thread-pool）？" class="headerlink" title="什么是线程池（thread pool）？"></a>什么是线程池（thread pool）？</h2><p>答：在面向对象编程中，创建和销毁对象是很费时间的，因为创建一个对象要获取内存资源或者其它更多资源。在 Java 中更是如此，虚拟机将试图跟踪每一个对象，以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数，特别是一些很耗资源的对象创建和销毁，这就是“池化资源”技术产生的原因。线程池顾名思义就是事先创建若干个可执行的线程放入一个池（容器）中，需要的时候从池中获取线程不用自行创建，使用完毕不需要销毁线程而是放回池中，从而减少创建和销毁线程对象的开销。</p><p>Java 5+ 中的 <code>Executor</code> 接口定义一个执行线程的工具。它的子类型即线程池接口是 <code>ExecutorService</code>。要配置一个线程池是比较复杂的，尤其是对于线程池的原理不是很清楚的情况下，因此在工具类 <code>Executors</code> 里面提供了一些静态工厂方法，生成一些常用的线程池，如下所示：</p><ul><li><code>newSingleThreadExecutor</code>：创建一个单线程的线程池。这个线程池只有一个线程在工作，也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束，那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。</li><li><code>newFixedThreadPool</code>：创建固定大小的线程池。每次提交一个任务就创建一个线程，直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变，如果某个线程因为执行异常而结束，那么线程池会补充一个新线程。</li><li><code>newCachedThreadPool</code>：创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程，那么就会回收部分空闲（60 秒不执行任务）的线程，当任务数增加时，此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制，线程池大小完全依赖于操作系统（或者说 JVM）能够创建的最大线程大小。</li><li><code>newScheduledThreadPool</code>：创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。</li><li><code>newSingleThreadScheduledExecutor</code>：创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。</li></ul><p>第 60 题的例子中演示了通过 <code>Executors</code> 工具类创建线程池并使用线程池执行线程的代码。如果希望在服务器上使用线程池，强烈建议使用 <code>newFixedThreadPool</code> 方法来创建线程池，这样能获得更好的性能。</p><h2 id="线程的基本状态以及状态之间的关系？"><a href="#线程的基本状态以及状态之间的关系？" class="headerlink" title="线程的基本状态以及状态之间的关系？"></a>线程的基本状态以及状态之间的关系？</h2><p>答：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/66.png" alt="线程的基本状态以及状态之间的关系"></p><p><strong>说明：</strong>其中 <code>Running</code> 表示运行状态，<code>Runnable</code> 表示就绪状态（万事俱备，只欠 CPU），<code>Blocked</code> 表示阻塞状态，阻塞状态又有多种情况，可能是因为调用 <code>wait()</code> 方法进入等待池，也可能是执行同步方法或同步代码块进入等锁池，或者是调用了 <code>sleep()</code> 方法或 <code>join()</code> 方法等待休眠或其他线程结束，或是因为发生了 I/O 中断。</p><h2 id="简述-synchronized-和-java-util-concurrent-locks-Lock-的异同？"><a href="#简述-synchronized-和-java-util-concurrent-locks-Lock-的异同？" class="headerlink" title="简述 synchronized 和 java.util.concurrent.locks.Lock 的异同？"></a>简述 <code>synchronized</code> 和 <code>java.util.concurrent.locks.Lock</code> 的异同？</h2><p>答：<code>Lock</code> 是 Java 5 以后引入的新的 API，和关键字 <code>synchronized</code> 相比主要相同点：<code>Lock</code> 能完成 <code>synchronized</code> 所实现的所有功能；主要不同点：<code>Lock</code> 有比 <code>synchronized</code> 更精确的线程语义和更好的性能，而且不强制性的要求一定要获得锁。<code>synchronized</code> 会自动释放锁，而 <code>Lock</code> 一定要求程序员手工释放，并且最好在 <code>finally</code> 块中释放（这是释放外部资源的最好的地方）。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Java/">Java</category>
      
      
      <comments>https://blog.eurkon.com/post/32cf50db.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Java 面试题解析（容器）</title>
      <link>https://blog.eurkon.com/post/d07acd09.html</link>
      <guid>https://blog.eurkon.com/post/d07acd09.html</guid>
      <pubDate>Wed, 17 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;List、Set、Map-是否继承自-Collection-接口？&quot;&gt;&lt;a href=&quot;#List、Set、Map-是否继承自-Collection-接口？&quot; class=&quot;headerlink&quot; title=&quot;List、Set、Map 是否继承自 Collect</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="List、Set、Map-是否继承自-Collection-接口？"><a href="#List、Set、Map-是否继承自-Collection-接口？" class="headerlink" title="List、Set、Map 是否继承自 Collection 接口？"></a><code>List</code>、<code>Set</code>、<code>Map</code> 是否继承自 <code>Collection</code> 接口？</h2><p>答：<code>List</code>、<code>Set</code> 是，<code>Map</code> 不是。<code>Map</code> 是键值对映射容器，与 <code>List</code> 和 <code>Set</code> 有明显的区别，而 <code>Set</code> 存储的零散的元素且不允许有重复元素（数学中的集合也是如此），<code>List</code> 是线性结构的容器，适用于按数值索引访问元素的情形。</p><h2 id="阐述-ArrayList、Vector、LinkedList-的存储性能和特性。"><a href="#阐述-ArrayList、Vector、LinkedList-的存储性能和特性。" class="headerlink" title="阐述 ArrayList、Vector、LinkedList 的存储性能和特性。"></a>阐述 <code>ArrayList</code>、<code>Vector</code>、<code>LinkedList</code> 的存储性能和特性。</h2><p>答：</p><ul><li><p><code>ArrayList</code> 和 <code>Vector</code> 都是使用<strong>数组方式</strong>存储数据，此数组元素数大于实际存储的数据以便增加和插入元素，它们都允许直接按序号索引元素，但是插入元素要涉及数组元素移动等内存操作，所以索引数据快而插入数据慢，<code>Vector</code> 中的方法由于添加了 <code>synchronized</code> 修饰，因此 <code>Vector</code> 是<strong>线程安全</strong>的容器，但性能上较 <code>ArrayList</code> 差，因此已经是 Java 中的遗留容器。</p></li><li><p><code>LinkedList</code> 使用<strong>双向链表</strong>实现存储（将内存中零散的内存单元通过附加的引用关联起来，形成一个可以按序号索引的线性结构，这种链式存储方式与数组的连续存储方式相比，内存的利用率更高），按序号索引数据需要进行前向或后向遍历，但是插入数据时只需要记录本项的前后项即可，所以插入速度较快。</p></li><li><p><code>Vector</code> 属于遗留容器（Java 早期的版本中提供的容器，除此之外，<code>Hashtable</code>、<code>Dictionary</code>、<code>BitSet</code>、<code>Stack</code>、<code>Properties</code> 都是遗留容器），已经不推荐使用，但是由于 <code>ArrayList</code> 和 <code>LinkedListed</code> 都是<strong>非线程安全</strong>的，如果遇到多个线程操作同一个容器的场景，则可以通过工具类 <code>Collections</code> 中的 <code>synchronizedList</code> 方法将其转换成线程安全的容器后再使用（这是对装潢模式的应用，将已有对象传入另一个类的构造器中创建新的对象来增强实现）。</p></li></ul><p><strong>补充：</strong>遗留容器中的 <code>Properties</code> 类和 <code>Stack</code> 类在设计上有严重的问题，<code>Properties</code> 是一个键和值都是字符串的特殊的键值对映射，在设计上应该是关联一个 <code>Hashtable</code> 并将其两个泛型参数设置为 <code>String</code> 类型，但是 Java API 中的 <code>Properties</code> 直接继承了 <code>Hashtable</code>，这很明显是对继承的滥用。这里复用代码的方式应该是 Has-A 关系而不是 Is-A 关系，另一方面容器都属于工具类，继承工具类本身就是一个错误的做法，使用工具类最好的方式是 Has-A 关系（关联）或 Use-A 关系（依赖）。同理，<code>Stack</code> 类继承 <code>Vector</code> 也是不正确的。Sun 公司的工程师们也会犯这种低级错误，让人唏嘘不已。</p><h2 id="Collection-和-Collections-的区别？"><a href="#Collection-和-Collections-的区别？" class="headerlink" title="Collection 和 Collections 的区别？"></a><code>Collection</code> 和 <code>Collections</code> 的区别？</h2><p>答：<code>Collection</code> 是一个接口，它是 <code>Set</code>、<code>List</code> 等容器的父接口；<code>Collections</code> 是个一个工具类，提供了一系列的静态方法来辅助容器操作，这些方法包括对容器的搜索、排序、线程安全化等等。</p><h2 id="List、Map、Set-三个接口存取元素时，各有什么特点？"><a href="#List、Map、Set-三个接口存取元素时，各有什么特点？" class="headerlink" title="List、Map、Set 三个接口存取元素时，各有什么特点？"></a><code>List</code>、<code>Map</code>、<code>Set</code> 三个接口存取元素时，各有什么特点？</h2><p>答：<code>List</code> 以特定索引来存取元素，可以有重复元素。<code>Set</code> 不能存放重复元素（用对象的 <code>equals()</code> 方法来区分元素是否重复）。<code>Map</code> 保存键值对（key-value pair）映射，映射关系可以是一对一或多对一。<code>Set</code> 和 <code>Map</code> 容器都有基于哈希存储和排序树的两种实现版本，基于哈希存储的版本理论存取时间复杂度为 <code>O(1)</code>，而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键（key）构成排序树从而达到排序和去重的效果。</p><h2 id="TreeMap-和-TreeSet-在排序时如何比较元素？Collections工具类中的sort-方法如何比较元素？"><a href="#TreeMap-和-TreeSet-在排序时如何比较元素？Collections工具类中的sort-方法如何比较元素？" class="headerlink" title="TreeMap 和 TreeSet 在排序时如何比较元素？Collections工具类中的sort()方法如何比较元素？"></a><code>TreeMap</code> 和 <code>TreeSet</code> 在排序时如何比较元素？<code>Collections</code>工具类中的<code>sort()</code>方法如何比较元素？</h2><p>答：<code>TreeSet</code> 要求存放的对象所属的类必须实现 <code>Comparable</code> 接口，该接口提供了比较元素的 <code>compareTo()</code> 方法，当插入元素时会回调该方法比较元素的大小。<code>TreeMap</code> 要求存放的键值对映射的键必须实现 <code>Comparable</code> 接口从而根据键对元素进行排序。<code>Collections</code> 工具类的 <code>sort()</code> 方法有两种重载的形式，第一种要求传入的待排序容器中存放的对象比较实现 <code>Comparable</code> 接口以实现元素的比较；第二种不强制性的要求容器中的元素必须可比较，但是要求传入第二个参数，参数是 <code>Comparator</code> 接口的子类型（需要重写 <code>compare</code> 方法实现元素的比较），相当于一个临时定义的排序规则，其实就是通过接口注入比较元素大小的算法，也是对回调模式的应用（Java 中对函数式编程的支持）。例子 1：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Student</span> <span class="keyword">implements</span> <span class="title">Comparable</span>&lt;<span class="title">Student</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;        <span class="comment">// 姓名</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> age;            <span class="comment">// 年龄</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Student</span><span class="params">(String name, <span class="keyword">int</span> age)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Student [name=&quot;</span> + name + <span class="string">&quot;, age=&quot;</span> + age + <span class="string">&quot;]&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">compareTo</span><span class="params">(Student o)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.age - o.age; <span class="comment">// 比较年龄(年龄的升序)</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Set;</span><br><span class="line"><span class="keyword">import</span> java.util.TreeSet;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test01</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Set&lt;Student&gt; set = <span class="keyword">new</span> TreeSet&lt;&gt;();     <span class="comment">// Java 7 的钻石语法(构造器后面的尖括号中不需要写类型)</span></span><br><span class="line">        set.add(<span class="keyword">new</span> Student(<span class="string">&quot;Hao LUO&quot;</span>, <span class="number">33</span>));</span><br><span class="line">        set.add(<span class="keyword">new</span> Student(<span class="string">&quot;XJ WANG&quot;</span>, <span class="number">32</span>));</span><br><span class="line">        set.add(<span class="keyword">new</span> Student(<span class="string">&quot;Bruce LEE&quot;</span>, <span class="number">60</span>));</span><br><span class="line">        set.add(<span class="keyword">new</span> Student(<span class="string">&quot;Bob YANG&quot;</span>, <span class="number">22</span>));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(Student stu : set) &#123;</span><br><span class="line">            System.out.println(stu);</span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">//      输出结果：</span></span><br><span class="line"><span class="comment">//      Student [name=Bob YANG, age=22]</span></span><br><span class="line"><span class="comment">//      Student [name=XJ WANG, age=32]</span></span><br><span class="line"><span class="comment">//      Student [name=Hao LUO, age=33]</span></span><br><span class="line"><span class="comment">//      Student [name=Bruce LEE, age=60]</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>例子 2：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Student</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String name;    <span class="comment">// 姓名</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> age;        <span class="comment">// 年龄</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Student</span><span class="params">(String name, <span class="keyword">int</span> age)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取学生姓名</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取学生年龄</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getAge</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Student [name=&quot;</span> + name + <span class="string">&quot;, age=&quot;</span> + age + <span class="string">&quot;]&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.Collections;</span><br><span class="line"><span class="keyword">import</span> java.util.Comparator;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test02</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        List&lt;Student&gt; list = <span class="keyword">new</span> ArrayList&lt;&gt;();     <span class="comment">// Java 7 的钻石语法(构造器后面的尖括号中不需要写类型)</span></span><br><span class="line">        list.add(<span class="keyword">new</span> Student(<span class="string">&quot;Hao LUO&quot;</span>, <span class="number">33</span>));</span><br><span class="line">        list.add(<span class="keyword">new</span> Student(<span class="string">&quot;XJ WANG&quot;</span>, <span class="number">32</span>));</span><br><span class="line">        list.add(<span class="keyword">new</span> Student(<span class="string">&quot;Bruce LEE&quot;</span>, <span class="number">60</span>));</span><br><span class="line">        list.add(<span class="keyword">new</span> Student(<span class="string">&quot;Bob YANG&quot;</span>, <span class="number">22</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 通过 sort 方法的第二个参数传入一个 Comparator 接口对象</span></span><br><span class="line">        <span class="comment">// 相当于是传入一个比较对象大小的算法到 sort 方法中</span></span><br><span class="line">        <span class="comment">// 由于 Java 中没有函数指针、仿函数、委托这样的概念</span></span><br><span class="line">        <span class="comment">// 因此要将一个算法传入一个方法中唯一的选择就是通过接口回调</span></span><br><span class="line">        Collections.sort(list, <span class="keyword">new</span> Comparator&lt;Student&gt; () &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">compare</span><span class="params">(Student o1, Student o2)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">return</span> o1.getName().compareTo(o2.getName());    <span class="comment">// 比较学生姓名</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(Student stu : list) &#123;</span><br><span class="line">            System.out.println(stu);</span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">//      输出结果: </span></span><br><span class="line"><span class="comment">//      Student [name=Bob YANG, age=22]</span></span><br><span class="line"><span class="comment">//      Student [name=Bruce LEE, age=60]</span></span><br><span class="line"><span class="comment">//      Student [name=Hao LUO, age=33]</span></span><br><span class="line"><span class="comment">//      Student [name=XJ WANG, age=32]</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Java/">Java</category>
      
      
      <comments>https://blog.eurkon.com/post/d07acd09.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Java 面试题解析（基础）</title>
      <link>https://blog.eurkon.com/post/11ea51ea.html</link>
      <guid>https://blog.eurkon.com/post/11ea51ea.html</guid>
      <pubDate>Tue, 16 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;面向对象的特征有哪些方面？&quot;&gt;&lt;a href=&quot;#面向对象的特征有哪些方面？&quot; class=&quot;headerlink&quot; title=&quot;面向对象的特征有哪些方面？&quot;&gt;&lt;/a&gt;面向对象的特征有哪些方面？&lt;/h2&gt;&lt;p&gt;答：面向对象的特征主要有以下几个方面：&lt;/p&gt;
&lt;u</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="面向对象的特征有哪些方面？"><a href="#面向对象的特征有哪些方面？" class="headerlink" title="面向对象的特征有哪些方面？"></a>面向对象的特征有哪些方面？</h2><p>答：面向对象的特征主要有以下几个方面：</p><ul><li><p>抽象：抽象是将一类对象的共同特征总结出来构造类的过程，包括<strong>数据抽象</strong>和<strong>行为抽象</strong>两方面。抽象只关注对象有哪些属性和行为，并不关注这些行为的细节是什么。</p></li><li><p>继承：继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类（超类、基类）；得到继承信息的类被称为子类（派生类）。继承让变化中的软件系统有了一定的延续性，同时继承也是封装程序中可变因素的重要手段。</p></li><li><p>封装：通常认为封装是把数据和操作数据的方法绑定起来，对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装；我们编写一个类就是对数据和数据操作的封装。可以说，封装就是隐藏一切可隐藏的东西，只向外界提供最简单的编程接口（可以想想普通洗衣机和全自动洗衣机的差别，明显全自动洗衣机封装更好因此操作起来更简单；我们现在使用的智能手机也是封装得足够好的，因为几个按键就搞定了所有的事情）。</p></li><li><p>多态性：多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为<strong>编译时的多态性</strong>和<strong>运行时的多态性</strong>。如果将对象的方法视为对象向外界提供的服务，那么运行时的多态性可以解释为：当 A 系统访问 B 系统提供的服务时，B 系统有多种提供服务的方式，但一切对 A 系统来说都是透明的（就像电动剃须刀是 A 系统，它的供电系统是 B 系统，B 系统可以使用电池供电或者用交流电，甚至还有可能是太阳能，A 系统只会通过 B 类对象调用供电的方法，但并不知道供电系统的底层实现是什么，究竟通过何种方式获得了动力）。</p><ul><li><strong>方法重载</strong>（<code>overload</code>）实现的是编译时的多态性（也称为前绑定），而<strong>方法重写</strong>（<code>override</code>）实现的是运行时的多态性（也称为后绑定）。</li><li>运行时的多态是面向对象最精髓的东西，要实现多态需要做两件事：<ol><li>方法重写（子类继承父类并重写父类中已有的或抽象的方法）；</li><li>对象造型（用父类型引用引用子类型对象，这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为）。</li></ol></li></ul></li></ul><h2 id="访问修饰符-public、private、protected、以及不写（默认）时的区别？"><a href="#访问修饰符-public、private、protected、以及不写（默认）时的区别？" class="headerlink" title="访问修饰符 public、private、protected、以及不写（默认）时的区别？"></a>访问修饰符 <code>public</code>、<code>private</code>、<code>protected</code>、以及不写（默认）时的区别？</h2><p>答：</p><div class="table-container"><table><thead><tr><th>修饰符</th><th>当前类</th><th>同包</th><th>子类</th><th>其他包</th></tr></thead><tbody><tr><td>public</td><td>√</td><td>√</td><td>√</td><td>√</td></tr><tr><td>protected</td><td>√</td><td>√</td><td>√</td><td>×</td></tr><tr><td>default</td><td>√</td><td>√</td><td>×</td><td>×</td></tr><tr><td>private</td><td>√</td><td>×</td><td>×</td><td>×</td></tr></tbody></table></div><p>类的成员不写访问修饰时默认为 <code>default</code>。默认对于同一个包中的其他类相当于公开（<code>public</code>），对于不是同一个包中的其他类相当于私有（<code>private</code>）。受保护（<code>protected</code>）对子类相当于公开，对不是同一包中的没有父子关系的类相当于私有。Java 中，外部类的修饰符只能是 <code>public</code> 或默认，类的成员（包括内部类）的修饰符可以是以上四种。</p><h2 id="String-是最基本的数据类型吗？"><a href="#String-是最基本的数据类型吗？" class="headerlink" title="String 是最基本的数据类型吗？"></a><code>String</code> 是最基本的数据类型吗？</h2><p>答：不是。Java 中的基本数据类型只有 8 个：<code>byte</code>、<code>short</code>、<code>int</code>、<code>long</code>、<code>float</code>、<code>double</code>、<code>char</code>、<code>boolean</code>；除了基本类型（primitive type），剩下的都是引用类型（reference type），Java 5 以后引入的枚举类型（enum）也算是一种比较特殊的引用类型。</p><h2 id="float-f-3-4-是否正确？"><a href="#float-f-3-4-是否正确？" class="headerlink" title="float f = 3.4; 是否正确？"></a><code>float f = 3.4;</code> 是否正确？</h2><p>答：不正确。3.4 是双精度数，将双精度型（<code>double</code>）赋值给浮点型（<code>float</code>）属于<strong>下转型</strong>（down-casting，也称为窄化）会造成<strong>精度损失</strong>，因此需要<strong>强制类型转换</strong> <code>float f = (float)3.4;</code> 或者写成 <code>float f = 3.4F</code>;。</p><h2 id="short-s1-1-s1-s1-1-有错吗？short-s1-1-s1-1-有错吗？"><a href="#short-s1-1-s1-s1-1-有错吗？short-s1-1-s1-1-有错吗？" class="headerlink" title="short s1 = 1; s1 = s1 + 1; 有错吗？short s1 = 1; s1 += 1; 有错吗？"></a><code>short s1 = 1; s1 = s1 + 1;</code> 有错吗？<code>short s1 = 1; s1 += 1;</code> 有错吗？</h2><p>答：对于 <code>short s1 = 1; s1 = s1 + 1;</code> 由于 1 是 <code>int</code> 类型，因此 <code>s1 + 1</code> 运算结果也是 <code>int</code> 型，需要强制转换类型才能赋值给 <code>short</code> 型。而 <code>short s1 = 1; s1 += 1;</code> 可以正确编译，因为 <code>s1 += 1;</code> 相当于 <code>s1 = (short)(s1 + 1);</code> 其中有隐含的强制类型转换。</p><h2 id="Java-有没有-goto？"><a href="#Java-有没有-goto？" class="headerlink" title="Java 有没有 goto？"></a>Java 有没有 <code>goto</code>？</h2><p>答：<code>goto</code> 是 Java 中的<strong>保留字</strong>，在目前版本的 Java 中没有使用。（根据 James Gosling（Java 之父）编写的《The Java Programming Language》一书的附录中给出了一个 Java 关键字列表，其中有 <code>goto</code> 和 <code>const</code>，但是这两个是目前无法使用的关键字，因此有些地方将其称之为保留字，其实保留字这个词应该有更广泛的意义，因为熟悉 C 语言的程序员都知道，在系统类库中使用过的有特殊意义的单词或单词的组合都被视为保留字）。</p><h2 id="int-和-Integer-有什么区别？"><a href="#int-和-Integer-有什么区别？" class="headerlink" title="int 和 Integer 有什么区别？"></a><code>int</code> 和 <code>Integer</code> 有什么区别？</h2><p>答：Java 是一个近乎纯洁的面向对象编程语言，但是为了编程的方便还是引入了基本数据类型，但是为了能够将这些基本数据类型当成对象操作，Java 为每一个基本数据类型都引入了对应的包装类型（wrapper class），<code>int</code> 的包装类就是 <code>Integer</code>，从 Java 5 开始引入了<strong>自动装箱 / 拆箱</strong>机制，使得二者可以相互转换。</p><p>Java 为每个原始类型提供了包装类型：</p><ul><li>原始类型：<code>boolean</code>，<code>char</code>，<code>byte</code>，<code>short</code>，<code>int</code>，<code>long</code>，<code>float</code>，<code>double</code>。</li><li>包装类型：<code>Boolean</code>，<code>Character</code>，<code>Byte</code>，<code>Short</code>，<code>Integer</code>，<code>Long</code>，<code>Float</code>，<code>Double</code>。</li></ul><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AutoUnboxingTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Integer a = <span class="keyword">new</span> Integer(<span class="number">3</span>);</span><br><span class="line">        Integer b = <span class="number">3</span>;                  <span class="comment">// 将 3 自动装箱成 Integer 类型</span></span><br><span class="line">        <span class="keyword">int</span> c = <span class="number">3</span>;</span><br><span class="line">        System.out.println(a == b);     <span class="comment">// false 两个引用没有引用同一对象</span></span><br><span class="line">        System.out.println(a == c);     <span class="comment">// true a 自动拆箱成 int 类型再和 c 比较</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>最近还遇到一个面试题，也是和自动装箱和拆箱有点关系的，代码如下所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 整型字面量的值在 -128 到 127 之间直接引用常量池中的 Integer 对象，否则 new 新的 Integer 对象</span></span><br><span class="line">        Integer f1 = <span class="number">100</span>, f2 = <span class="number">100</span>, f3 = <span class="number">150</span>, f4 = <span class="number">150</span>;</span><br><span class="line">        System.out.println(f1 == f2);     <span class="comment">// true 直接引用常量池中的 Integer 对象</span></span><br><span class="line">        System.out.println(f3 == f4);     <span class="comment">// false 两个引用没有引用同一对象</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>如果不明就里很容易认为两个输出要么都是 <code>true</code> 要么都是 <code>false</code>。首先需要注意的是 <code>f1</code>、<code>f2</code>、<code>f3</code>、<code>f4</code> 四个变量都是 <code>Integer</code> 对象引用，所以下面的 <code>==</code> 运算比较的不是值而是引用。装箱的本质是什么呢？当我们给一个 <code>Integer</code> 对象赋一个 <code>int</code> 值的时候，会调用 <code>Integer</code> 类的静态方法 <code>valueOf</code>，如果看看 <code>valueOf</code> 的源代码就知道发生了什么。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Integer <span class="title">valueOf</span><span class="params">(<span class="keyword">int</span> i)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (i &gt;= IntegerCache.low &amp;&amp; i &lt;= IntegerCache.high)</span><br><span class="line">        <span class="keyword">return</span> IntegerCache.cache[i + (-IntegerCache.low)];</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Integer(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><code>IntegerCache</code> 是 <code>Integer</code> 的内部类，其代码如下所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">  * Cache to support the object identity semantics of autoboxing for values between</span></span><br><span class="line"><span class="comment">  * -128 and 127 (inclusive) as required by JLS.</span></span><br><span class="line"><span class="comment">  *</span></span><br><span class="line"><span class="comment">  * The cache is initialized on first usage.  The size of the cache</span></span><br><span class="line"><span class="comment">  * may be controlled by the &#123;<span class="doctag">@code</span> -XX:AutoBoxCacheMax=&lt;size&gt;&#125; option.</span></span><br><span class="line"><span class="comment">  * During VM initialization, java.lang.Integer.IntegerCache.high property</span></span><br><span class="line"><span class="comment">  * may be set and saved in the private system properties in the</span></span><br><span class="line"><span class="comment">  * sun.misc.VM class.</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">IntegerCache</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> low = -<span class="number">128</span>;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> high;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> Integer cache[];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="comment">// high value may be configured by property</span></span><br><span class="line">        <span class="keyword">int</span> h = <span class="number">127</span>;</span><br><span class="line">        String integerCacheHighPropValue =</span><br><span class="line">            sun.misc.VM.getSavedProperty(<span class="string">&quot;java.lang.Integer.IntegerCache.high&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (integerCacheHighPropValue != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">int</span> i = parseInt(integerCacheHighPropValue);</span><br><span class="line">                i = Math.max(i, <span class="number">127</span>);</span><br><span class="line">                <span class="comment">// Maximum array size is Integer.MAX_VALUE</span></span><br><span class="line">                h = Math.min(i, Integer.MAX_VALUE - (-low) -<span class="number">1</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span>( NumberFormatException nfe) &#123;</span><br><span class="line">                <span class="comment">// If the property cannot be parsed into an int, ignore it.</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        high = h;</span><br><span class="line"></span><br><span class="line">        cache = <span class="keyword">new</span> Integer[(high - low) + <span class="number">1</span>];</span><br><span class="line">        <span class="keyword">int</span> j = low;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> k = <span class="number">0</span>; k &lt; cache.length; k++)</span><br><span class="line">            cache[k] = <span class="keyword">new</span> Integer(j++);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// range [-128, 127] must be interned (JLS7 5.1.7)</span></span><br><span class="line">        <span class="keyword">assert</span> IntegerCache.high &gt;= <span class="number">127</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">IntegerCache</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>简单的说，如果整型字面量的值在 <code>-128</code> 到 <code>127</code> 之间，那么不会 <code>new</code> 新的 <code>Integer</code> 对象，而是直接引用常量池中的 <code>Integer</code> 对象，所以上面的面试题中 <code>f1 == f2</code> 的结果是 <code>true</code>，而 <code>f3==f4</code> 的结果是 <code>false</code>。</p><p><strong>注意：</strong>越是貌似简单的面试题其中的玄机就越多，需要面试者有相当深厚的功力。</p><h2 id="amp-和-amp-amp-的区别？"><a href="#amp-和-amp-amp-的区别？" class="headerlink" title="&amp; 和 &amp;&amp; 的区别？"></a><code>&amp;</code> 和 <code>&amp;&amp;</code> 的区别？</h2><p>答：<code>&amp;</code> 运算符有两种用法：(1)<strong>按位与</strong>；(2)<strong>逻辑与</strong>。<code>&amp;&amp;</code> 运算符是<strong>短路与</strong>运算。逻辑与跟短路与的差别是非常巨大的，虽然二者都要求运算符左右两端的布尔值都是 <code>true</code> 整个表达式的值才是 <code>true</code>。<code>&amp;&amp;</code> 之所以称为短路运算是因为，如果 <code>&amp;&amp;</code> 左边的表达式的值是 <code>false</code>，右边的表达式会被直接短路掉，不会进行运算。很多时候我们可能都需要用 <code>&amp;&amp;</code> 而不是 <code>&amp;</code>，例如在验证用户登录时判定用户名不是 <code>null</code> 而且不是空字符串，应当写为：<code>username != null &amp;&amp;!username.equals(&quot;&quot;)</code>，二者的顺序不能交换，更不能用 <code>&amp;</code> 运算符，因为第一个条件如果不成立，根本不能进行字符串的 <code>equals</code> 比较，否则会产生 <code>NullPointerException</code> 异常。<strong>注意：</strong>逻辑或运算符（<code>|</code>）和短路或运算符（<code>||</code>）的差别也是如此。</p><p><strong>补充：</strong>如果你熟悉 JavaScript，那你可能更能感受到短路运算的强大，想成为 JavaScript 的高手就先从玩转短路运算开始吧。</p><h2 id="解释内存中的栈（stack）、堆（heap）和方法区（method-area）的用法。"><a href="#解释内存中的栈（stack）、堆（heap）和方法区（method-area）的用法。" class="headerlink" title="解释内存中的栈（stack）、堆（heap）和方法区（method area）的用法。"></a>解释内存中的栈（stack）、堆（heap）和方法区（method area）的用法。</h2><p>答：通常我们定义一个基本数据类型的变量，一个对象的引用，还有就是函数调用的现场保存都使用 JVM 中的栈空间；而通过 <code>new</code> 关键字和构造器创建的对象则放在堆空间，堆是垃圾收集器管理的主要区域，由于现在的垃圾收集器都采用分代收集算法，所以堆空间还可以细分为新生代和老生代，再具体一点可以分为 Eden、Survivor（又可分为 From Survivor 和 To Survivor）、Tenured；方法区和堆都是各个线程共享的内存区域，用于存储已经被 JVM 加载的类信息、常量、静态变量、JIT 编译器编译后的代码等数据；程序中的字面量（literal）如直接书写的 <code>100</code>、<code>&quot;hello&quot;</code> 和常量都是放在常量池中，常量池是方法区的一部分。栈空间操作起来最快但是栈很小，通常大量的对象都是放在堆空间，栈和堆的大小都可以通过 JVM 的启动参数来进行调整，栈空间用光了会引发 <code>StackOverflowError</code>，而堆和常量池空间不足则会引发 <code>OutOfMemoryError</code>。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String str = <span class="keyword">new</span> String(<span class="string">&quot;hello&quot;</span>);</span><br></pre></td></tr></table></figure></p><p>上面的语句中变量 <code>str</code> 放在栈上，用 <code>new</code> 创建出来的字符串对象放在堆上，而 <code>&quot;hello&quot;</code> 这个字面量是放在方法区的。</p><p><strong>补充 1：</strong>较新版本的 Java（从 Java 6 的某个更新开始）中，由于 JIT 编译器的发展和“逃逸分析”技术的逐渐成熟，栈上分配、标量替换等优化技术使得对象一定分配在堆上这件事情已经变得不那么绝对了。</p><p><strong>补充 2：</strong>运行时常量池相当于 Class 文件常量池具有动态性，Java 语言并不要求常量一定只有编译期间才能产生，运行期间也可以将新的常量放入池中，<code>String</code> 类的 <code>intern()</code> 方法就是这样的。</p><p>看看下面代码的执行结果是什么并且比较一下 Java 7 以前和以后的运行结果是否一致。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">String s1 = <span class="keyword">new</span> StringBuilder(<span class="string">&quot;go&quot;</span>).append(<span class="string">&quot;od&quot;</span>).toString();</span><br><span class="line">System.out.println(s1.intern() == s1);</span><br><span class="line"></span><br><span class="line">String s2 = <span class="keyword">new</span> StringBuilder(<span class="string">&quot;ja&quot;</span>).append(<span class="string">&quot;va&quot;</span>).toString();</span><br><span class="line">System.out.println(s2.intern() == s2);</span><br></pre></td></tr></table></figure></p><h2 id="Math-round-11-5-等于多少？Math-round-11-5-等于多少？"><a href="#Math-round-11-5-等于多少？Math-round-11-5-等于多少？" class="headerlink" title="Math.round(11.5) 等于多少？Math.round(-11.5) 等于多少？"></a><code>Math.round(11.5)</code> 等于多少？<code>Math.round(-11.5)</code> 等于多少？</h2><p>答：<code>Math.round(11.5)</code> 的返回值是 <code>12</code>，<code>Math.round(-11.5)</code> 的返回值是 <code>-11</code>。四舍五入的原理是在参数上加 <code>0.5</code> 然后进行下取整。</p><h2 id="switch-是否能作用在-byte-上，是否能作用在-long-上，是否能作用在-String-上？"><a href="#switch-是否能作用在-byte-上，是否能作用在-long-上，是否能作用在-String-上？" class="headerlink" title="switch 是否能作用在 byte 上，是否能作用在 long 上，是否能作用在 String 上？"></a><code>switch</code> 是否能作用在 <code>byte</code> 上，是否能作用在 <code>long</code> 上，是否能作用在 <code>String</code> 上？</h2><p>答：在 Java 5 以前，<code>switch(expr)</code> 中，<code>expr</code> 只能是 <code>byte</code>、<code>short</code>、<code>char</code>、<code>int</code>。从 Java 5 开 始，Java 中引入了枚举类型，<code>expr</code> 也可以是 <code>enum</code> 类型，从 Java 7 开始，<code>expr</code> 还可以是字符串（<code>String</code>），但是长整型（<code>long</code>）在目前所有的版本中都是不可以的。</p><h2 id="用最有效率的方法计算-2-乘以-8？"><a href="#用最有效率的方法计算-2-乘以-8？" class="headerlink" title="用最有效率的方法计算 2 乘以 8？"></a>用最有效率的方法计算 2 乘以 8？</h2><p>答：<code>2 &lt;&lt; 3</code>（左移 3 位相当于乘以 2 的 3 次方，右移 3 位相当于除以 2 的 3 次方）。</p><p><strong>补充：</strong>我们为编写的类重写 <code>hashCode</code> 方法时，可能会看到如下所示的代码，其实我们不太理解为什么要使用这样的乘法运算来产生哈希码（散列码），而且为什么这个数是个素数，为什么通常选择 31 这个数？前两个问题的答案你可以自己百度一下，选择 31 是因为可以用移位和减法运算来代替乘法，从而得到更好的性能。说到这里你可能已经想到了：<code>31 * num</code> 等价于 <code>(num &lt;&lt; 5) - num</code>，左移 5 位相当于乘以 2 的 5 次方再减去自身就相当于乘以 31，现在的 JVM 都能自动完成这个优化。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PhoneNumber</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> areaCode;</span><br><span class="line">    <span class="keyword">private</span> String prefix;</span><br><span class="line">    <span class="keyword">private</span> String lineNumber;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">hashCode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> prime = <span class="number">31</span>;</span><br><span class="line">        <span class="keyword">int</span> result = <span class="number">1</span>;</span><br><span class="line">        result = prime * result + areaCode;</span><br><span class="line">        result = prime * result</span><br><span class="line">                + ((lineNumber == <span class="keyword">null</span>) ? <span class="number">0</span> : lineNumber.hashCode());</span><br><span class="line">        result = prime * result + ((prefix == <span class="keyword">null</span>) ? <span class="number">0</span> : prefix.hashCode());</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">equals</span><span class="params">(Object obj)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span> == obj)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        <span class="keyword">if</span> (obj == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (getClass() != obj.getClass())</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        PhoneNumber other = (PhoneNumber) obj;</span><br><span class="line">        <span class="keyword">if</span> (areaCode != other.areaCode)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (lineNumber == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (other.lineNumber != <span class="keyword">null</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!lineNumber.equals(other.lineNumber))</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (prefix == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (other.prefix != <span class="keyword">null</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!prefix.equals(other.prefix))</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="数组有没有-length-方法？String-有没有-length-方法？"><a href="#数组有没有-length-方法？String-有没有-length-方法？" class="headerlink" title="数组有没有 length() 方法？String 有没有 length() 方法？"></a>数组有没有 <code>length()</code> 方法？String 有没有 <code>length()</code> 方法？</h2><p>答：数组没有 <code>length()</code> 方法，有 <code>length</code> 的属性。<code>String</code> 有 <code>length()</code> 方法。<code>JavaScript</code> 中，获得字符串的长度是通过 <code>length</code> 属性得到的，这一点容易和 Java 混淆。</p><h2 id="在-Java-中，如何跳出当前的多重嵌套循环？"><a href="#在-Java-中，如何跳出当前的多重嵌套循环？" class="headerlink" title="在 Java 中，如何跳出当前的多重嵌套循环？"></a>在 Java 中，如何跳出当前的多重嵌套循环？</h2><p>答：在最外层循环前加一个标记如 <code>A</code>，然后用 <code>break A;</code> 可以跳出多重循环。（Java 中支持带标签的 <code>break</code> 和 <code>continue</code> 语句，作用有点类似于 C 和 C++ 中的 <code>goto</code> 语句，但是就像要避免使用 <code>goto</code> 一样，应该避免使用带标签的 <code>break</code> 和 <code>continue</code>，因为它不会让你的程序变得更优雅，很多时候甚至有相反的作用，所以这种语法其实不知道更好）</p><h2 id="构造器（constructor）是否可被重写（override）？"><a href="#构造器（constructor）是否可被重写（override）？" class="headerlink" title="构造器（constructor）是否可被重写（override）？"></a>构造器（<code>constructor</code>）是否可被重写（<code>override</code>）？</h2><p>答：构造器不能被继承，因此不能被重写，但可以被重载。</p><h2 id="两个对象值相同-x-equals-y-true-，但却可有不同的-hash-code，这句话对不对？"><a href="#两个对象值相同-x-equals-y-true-，但却可有不同的-hash-code，这句话对不对？" class="headerlink" title="两个对象值相同 (x.equals(y) == true)，但却可有不同的 hash code，这句话对不对？"></a>两个对象值相同 (<code>x.equals(y) == true</code>)，但却可有不同的 <code>hash code</code>，这句话对不对？</h2><p>答：不对，如果两个对象 <code>x</code> 和 <code>y</code> 满足 <code>x.equals(y) == true</code>，它们的哈希码（hash code）应当相同。Java 对于 <code>eqauls</code> 方法和 <code>hashCode</code> 方法是这样规定的：</p><ol><li>如果两个对象相同（<code>equals</code> 方法返回 <code>true</code>），那么它们的 <code>hashCode</code> 值一定要相同；</li><li>如果两个对象的 <code>hashCode</code> 相同，它们并不一定相同。</li></ol><p>当然，你未必要按照要求去做，但是如果你违背了上述原则就会发现在使用容器时，相同的对象可以出现在 <code>Set</code> 集合中，同时增加新元素的效率会大大下降（对于使用哈希存储的系统，如果哈希码频繁的冲突将会造成存取性能急剧下降）。</p><p><strong>补充：</strong>关于 <code>equals</code> 和 <code>hashCode</code> 方法，很多 Java 程序员都知道，但很多人也就是仅仅知道而已，在 Joshua Bloch 的大作《Effective Java》（很多软件公司，《Effective Java》、《Java 编程思想》以及《重构：改善既有代码质量》是 Java 程序员必看书籍，如果你还没看过，那就赶紧去亚马逊买一本吧）中是这样介绍 <code>equals</code> 方法的：首先 <code>equals</code> 方法必须满足<strong>自反性</strong>（<code>x.equals(x)</code> 必须返回 <code>true</code>）、<strong>对称性</strong>（ <code>x.equals(y)</code> 返回 <code>true</code> 时，<code>y.equals(x)</code> 也必须返回 <code>true</code>）、<strong>传递性</strong>（<code>x.equals(y)</code> 和 <code>y.equals(z)</code> 都返回 <code>true</code> 时，<code>x.equals(z)</code> 也必须返回 <code>true</code>）和<strong>一致性</strong>（当 <code>x</code> 和 <code>y</code> 引用的对象信息没有被修改时，多次调用 <code>x.equals(y)</code> 应该得到同样的返回值），而且对于任何非 <code>null</code> 值的引用 <code>x</code>，<code>x.equals(null)</code> 必须返回 <code>false</code>。实现高质量的 <code>equals</code> 方法的诀窍包括：</p><ol><li>使用 <code>==</code> 操作符检查“参数是否为这个对象的引用”；</li><li>使用 <code>instanceof</code> 操作符检查“参数是否为正确的类型”；</li><li>对于类中的关键属性，检查参数传入对象的属性是否与之相匹配；</li><li>编写完 <code>equals</code> 方法后，问自己它是否满足对称性、传递性、一致性；</li><li>重写 <code>equals</code> 时总是要重写 <code>hashCode</code>；</li><li>不要将 <code>equals</code> 方法参数中的 <code>Object</code> 对象替换为其他的类型，在重写时不要忘掉 <code>@Override</code> 注解。</li></ol><h2 id="是否可以继承-String-类？"><a href="#是否可以继承-String-类？" class="headerlink" title="是否可以继承 String 类？"></a>是否可以继承 <code>String</code> 类？</h2><p>答：<code>String</code> 类是 <code>final</code> 类，不可以被继承。</p><p><strong>补充：</strong>继承 <code>String</code> 本身就是一个错误的行为，对 <code>String</code> 类型最好的重用方式是关联关系（Has-A）和依赖关系（Use-A）而不是继承关系（Is-A）。</p><h2 id="当一个对象被当作参数传递到一个方法后，此方法可改变这个对象的属性，并可返回变化后的结果，那么这里到底是值传递还是引用传递？"><a href="#当一个对象被当作参数传递到一个方法后，此方法可改变这个对象的属性，并可返回变化后的结果，那么这里到底是值传递还是引用传递？" class="headerlink" title="当一个对象被当作参数传递到一个方法后，此方法可改变这个对象的属性，并可返回变化后的结果，那么这里到底是值传递还是引用传递？"></a>当一个对象被当作参数传递到一个方法后，此方法可改变这个对象的属性，并可返回变化后的结果，那么这里到底是值传递还是引用传递？</h2><p>答：是值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时，参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变，但对对象引用的改变是不会影响到调用者的。C++ 和 C# 中可以通过传引用或传输出参数来改变传入的参数的值。在 C# 中可以编写如下所示的代码，但是在 Java 中却做不到。</p><p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">CS01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">class</span> <span class="title">Program</span> &#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">swap</span>(<span class="params"><span class="keyword">ref</span> <span class="built_in">int</span> x, <span class="keyword">ref</span> <span class="built_in">int</span> y</span>)</span> &#123;</span><br><span class="line">            <span class="built_in">int</span> temp = x;</span><br><span class="line">            x = y;</span><br><span class="line">            y = temp;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">Main</span> (<span class="params"><span class="built_in">string</span>[] args</span>)</span> &#123;</span><br><span class="line">            <span class="built_in">int</span> a = <span class="number">5</span>, b = <span class="number">10</span>;</span><br><span class="line">            swap (<span class="keyword">ref</span> a, <span class="keyword">ref</span> b);</span><br><span class="line">            <span class="comment">// a = 10, b = 5;</span></span><br><span class="line">            Console.WriteLine (<span class="string">&quot;a = &#123;0&#125;, b = &#123;1&#125;&quot;</span>, a, b);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>说明：</strong>Java 中没有传引用实在是非常的不方便，这一点在 Java 8 中仍然没有得到改进，正是如此在 Java 编写的代码中才会出现大量的 <code>Wrapper</code> 类（将需要通过方法调用修改的引用置于一个 <code>Wrapper</code> 类中，再将 <code>Wrapper</code> 对象传入方法），这样的做法只会让代码变得臃肿，尤其是让从 C 和 C++ 转型为 Java 程序员的开发者无法容忍。</p><h2 id="String-和-StringBuilder、StringBuffer-的区别？"><a href="#String-和-StringBuilder、StringBuffer-的区别？" class="headerlink" title="String 和 StringBuilder、StringBuffer 的区别？"></a><code>String</code> 和 <code>StringBuilder</code>、<code>StringBuffer</code> 的区别？</h2><p>答：Java 平台提供了两种类型的字符串：<code>String</code> 和 <code>StringBuffer/StringBuilder</code>，它们可以储存和操作字符串。其中 <code>String</code> 是只读字符串，也就意味着 String 引用的字符串内容是不能被改变的。而 <code>StringBuffer/StringBuilder</code> 类表示的字符串对象可以直接进行修改。<code>StringBuilder</code> 是 Java 5 中引入的，它和 <code>StringBuffer</code> 的方法完全相同，区别在于它是在单线程环境下使用的，因为它的所有方面都没有被 <code>synchronized</code> 修饰，因此它的效率也比 <code>StringBuffer</code> 要高。</p><p>面试题 1 - 什么情况下用 <code>+</code> 运算符进行字符串连接比调用 <code>StringBuffer/StringBuilder</code> 对象的 <code>append</code> 方法连接字符串性能更好？</p><p>面试题 2 - 请说出下面程序的输出。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StringEqualTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        String s1 = <span class="string">&quot;Programming&quot;</span>;</span><br><span class="line">        String s2 = <span class="keyword">new</span> String(<span class="string">&quot;Programming&quot;</span>);</span><br><span class="line">        String s3 = <span class="string">&quot;Program&quot;</span>;</span><br><span class="line">        String s4 = <span class="string">&quot;ming&quot;</span>;</span><br><span class="line">        String s5 = <span class="string">&quot;Program&quot;</span> + <span class="string">&quot;ming&quot;</span>;</span><br><span class="line">        String s6 = s3 + s4;</span><br><span class="line">        System.out.println(s1 == s2);             <span class="comment">// false</span></span><br><span class="line">        System.out.println(s1 == s5);             <span class="comment">// true</span></span><br><span class="line">        System.out.println(s1 == s6);             <span class="comment">// false</span></span><br><span class="line">        System.out.println(s1 == s6.intern());    <span class="comment">// true</span></span><br><span class="line">        System.out.println(s2 == s2.intern());    <span class="comment">// false</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>补充：</strong>解答上面的面试题需要清楚两点：</p><ol><li><p><code>String</code> 对象的 <code>intern</code> 方法会得到字符串对象在常量池中对应的版本的引用（如果常量池中有一个字符串与 <code>String</code> 对象的 <code>equals</code> 结果是 <code>true</code>），如果常量池中没有对应的字符串，则该字符串将被添加到常量池中，然后返回常量池中字符串的引用；</p></li><li><p>字符串的 <code>+</code> 操作其本质是创建了 <code>StringBuilder</code> 对象进行 <code>append</code> 操作，然后将拼接后的 <code>StringBuilder</code> 对象用 <code>toString</code> 方法处理成 <code>String</code> 对象，这一点可以用 <code>javap -c StringEqualTest.class</code> 命令获得 class 文件对应的 JVM 字节码指令就可以看出来。</p></li></ol><h2 id="重载（Overload）和重写（Override）的区别。重载的方法能否根据返回类型进行区分？"><a href="#重载（Overload）和重写（Override）的区别。重载的方法能否根据返回类型进行区分？" class="headerlink" title="重载（Overload）和重写（Override）的区别。重载的方法能否根据返回类型进行区分？"></a>重载（Overload）和重写（Override）的区别。重载的方法能否根据返回类型进行区分？</h2><p>答：方法的重载和重写都是实现多态的方式，区别在于前者实现的是编译时的多态性，而后者实现的是运行时的多态性。重载发生在一个类中，同名的方法如果有不同的参数列表（参数类型不同、参数个数不同或者二者都不同）则视为重载；重写发生在子类与父类之间，重写要求子类被重写方法与父类被重写方法有相同的返回类型，比父类被重写方法更好访问，不能比父类被重写方法声明更多的异常（里氏代换原则）。重载对返回类型没有特殊的要求。</p><p>面试题：华为的面试题中曾经问过这样一个问题 - &quot;为什么不能根据返回类型来区分重载&quot;，快说出你的答案吧！</p><h2 id="描述一下-JVM-加载-class-文件的原理机制？"><a href="#描述一下-JVM-加载-class-文件的原理机制？" class="headerlink" title="描述一下 JVM 加载 class 文件的原理机制？"></a>描述一下 JVM 加载 <code>class</code> 文件的原理机制？</h2><p>答：JVM 中类的装载是由类加载器（ClassLoader）和它的子类来实现的，Java 中的类加载器是一个重要的 Java 运行时系统组件，它负责在运行时查找和装入类文件中的类。</p><p>由于 Java 的跨平台性，经过编译的 Java 源程序并不是一个可执行程序，而是一个或多个类文件。当 Java 程序需要使用某个类时，JVM 会确保这个类已经被加载、连接（验证、准备和解析）和初始化。类的加载是指把类的 <code>.class</code> 文件中的数据读入到内存中，通常是创建一个字节数组读入 <code>.class</code> 文件，然后产生与所加载类对应的 <code>Class</code> 对象。加载完成后，<code>Class</code> 对象还不完整，所以此时的类还不可用。当类被加载后就进入连接阶段，这一阶段包括<strong>验证</strong>、<strong>准备</strong>（为静态变量分配内存并设置默认的初始值）和<strong>解析</strong>（将符号引用替换为直接引用）三个步骤。最后 JVM 对类进行初始化，包括：(1)如果类存在直接的父类并且这个类还没有被初始化，那么就先初始化父类；(2)如果类中存在初始化语句，就依次执行这些初始化语句。</p><p>类的加载是由类加载器完成的，类加载器包括：根加载器（BootStrap）、扩展加载器（Extension）、系统加载器（System）和用户自定义类加载器（<code>java.lang.ClassLoader</code> 的子类）。从 Java 2（JDK 1.2）开始，类加载过程采取了父亲委托机制（PDM）。PDM 更好的保证了 Java 平台的安全性，在该机制中，JVM 自带的 Bootstrap 是根加载器，其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载，父类加载器无能为力时才由其子类加载器自行加载。JVM 不会向 Java 程序提供对 Bootstrap 的引用。下面是关于几个类加载器的说明：</p><ul><li>Bootstrap：一般用本地代码实现，负责加载 JVM 基础核心类库（<code>rt.jar</code>）；</li><li>Extension：从 <code>java.ext.dirs</code> 系统属性所指定的目录中加载类库，它的父加载器是 Bootstrap；</li><li>System：又叫应用类加载器，其父类是 <code>Extension</code>。它是应用最广泛的类加载器。它从环境变量 <code>classpath</code> 或者系统属性 <code>java.class.path</code> 所指定的目录中记载类，是用户自定义加载器的默认父加载器。</li></ul><h2 id="char-类型变量中能不能存储一个中文汉字，为什么？"><a href="#char-类型变量中能不能存储一个中文汉字，为什么？" class="headerlink" title="char 类型变量中能不能存储一个中文汉字，为什么？"></a><code>char</code> 类型变量中能不能存储一个中文汉字，为什么？</h2><p>答：<code>char</code> 类型可以存储一个中文汉字，因为 Java 中使用的编码是 <code>Unicode</code>（不选择任何特定的编码，直接使用字符在字符集中的编号，这是统一的唯一方法），一个 <code>char</code> 类型占 2 个字节（16 比特），所以放一个中文是没问题的。</p><p><strong>补充：</strong>使用 <code>Unicode</code> 意味着字符在 JVM 内部和外部有不同的表现形式，在 JVM 内部都是 <code>Unicode</code>，当这个字符被从 JVM 内部转移到外部时（例如存入文件系统中），需要进行编码转换。所以 Java 中有字节流和字符流，以及在字符流和字节流之间进行转换的转换流，如 <code>InputStreamReader</code> 和 <code>OutputStreamReader</code>，这两个类是字节流和字符流之间的适配器类，承担了编码转换的任务；对于 C 程序员来说，要完成这样的编码转换恐怕要依赖于 <code>union</code>（联合体/共用体）共享内存的特征来实现了。</p><h2 id="抽象类（abstract-class）和接口（interface）有什么异同？"><a href="#抽象类（abstract-class）和接口（interface）有什么异同？" class="headerlink" title="抽象类（abstract class）和接口（interface）有什么异同？"></a>抽象类（abstract class）和接口（interface）有什么异同？</h2><p>答：抽象类和接口都不能够实例化，但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现，否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象，因为抽象类中可以定义构造器，可以有抽象方法和具体方法，而接口中不能定义构造器而且其中的方法全部都是抽象方法。抽象类中的成员可以是 <code>private</code>、<code>默认</code>、<code>protected</code>、<code>public</code> 的，而接口中的成员全都是 <code>public</code> 的。抽象类中可以定义成员变量，而接口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类，而抽象类未必要有抽象方法。</p><h2 id="静态嵌套类（Static-Nested-Class）和内部类（Inner-Class）的不同？"><a href="#静态嵌套类（Static-Nested-Class）和内部类（Inner-Class）的不同？" class="headerlink" title="静态嵌套类（Static Nested Class）和内部类（Inner Class）的不同？"></a>静态嵌套类（Static Nested Class）和内部类（Inner Class）的不同？</h2><p>答：Static Nested Class 是被声明为静态（<code>static</code>）的内部类，它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化，其语法看起来挺诡异的，如下所示。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 扑克类（一副扑克）</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Poker</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String[] suites = &#123;<span class="string">&quot;黑桃&quot;</span>, <span class="string">&quot;红桃&quot;</span>, <span class="string">&quot;草花&quot;</span>, <span class="string">&quot;方块&quot;</span>&#125;;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span>[] faces = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">11</span>, <span class="number">12</span>, <span class="number">13</span>&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Card[] cards;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构造器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Poker</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        cards = <span class="keyword">new</span> Card[<span class="number">52</span>];</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; suites.length; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; faces.length; j++) &#123;</span><br><span class="line">                cards[i * <span class="number">13</span> + j] = <span class="keyword">new</span> Card(suites[i], faces[j]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 洗牌 （随机乱序）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">shuffle</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>, len = cards.length; i &lt; len; i++) &#123;</span><br><span class="line">            <span class="keyword">int</span> index = (<span class="keyword">int</span>) (Math.random() * len);</span><br><span class="line">            Card temp = cards[index];</span><br><span class="line">            cards[index] = cards[i];</span><br><span class="line">            cards[i] = temp;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 发牌</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> index 发牌的位置</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Card <span class="title">deal</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> cards[index];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 卡片类（一张扑克）</span></span><br><span class="line"><span class="comment">     * [内部类]</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Card</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> String suite;   <span class="comment">// 花色</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">int</span> face;       <span class="comment">// 点数</span></span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Card</span><span class="params">(String suite, <span class="keyword">int</span> face)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.suite = suite;</span><br><span class="line">            <span class="keyword">this</span>.face = face;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            String faceStr = <span class="string">&quot;&quot;</span>;</span><br><span class="line">            <span class="keyword">switch</span>(face) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="number">1</span>: faceStr = <span class="string">&quot;A&quot;</span>; <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> <span class="number">11</span>: faceStr = <span class="string">&quot;J&quot;</span>; <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> <span class="number">12</span>: faceStr = <span class="string">&quot;Q&quot;</span>; <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> <span class="number">13</span>: faceStr = <span class="string">&quot;K&quot;</span>; <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">default</span>: faceStr = String.valueOf(face);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> suite + faceStr;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>测试代码：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PokerTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Poker poker = <span class="keyword">new</span> Poker();</span><br><span class="line">        poker.shuffle();                  <span class="comment">// 洗牌</span></span><br><span class="line">        Poker.Card c1 = poker.deal(<span class="number">0</span>);    <span class="comment">// 发第一张牌</span></span><br><span class="line">        <span class="comment">// 对于非静态内部类 Card</span></span><br><span class="line">        <span class="comment">// 只有通过其外部类 Poker 对象才能创建 Card 对象</span></span><br><span class="line">        Poker.Card c2 = poker.<span class="function">new <span class="title">Card</span><span class="params">(<span class="string">&quot;红心&quot;</span>, <span class="number">1</span>)</span></span>;    <span class="comment">// 自己创建一张牌</span></span><br><span class="line"></span><br><span class="line">        System.out.println(c1);           <span class="comment">// 洗牌后的第一张</span></span><br><span class="line">        System.out.println(c2);           <span class="comment">// 打印: 红心 A</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>面试题 - 下面的代码哪些地方会产生编译错误？</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Outer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">Inner</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">foo</span><span class="params">()</span> </span>&#123; <span class="keyword">new</span> Inner(); &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">bar</span><span class="params">()</span> </span>&#123; <span class="keyword">new</span> Inner(); &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> Inner();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>Java 中非静态内部类对象的创建要依赖其外部类对象，上面的面试题中 <code>foo</code> 和 <code>main</code> 方法都是静态方法，静态方法中没有 <code>this</code>，也就是说没有所谓的外部类对象，因此无法创建内部类对象，如果要在静态方法中创建内部类对象，可以这样做：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> Outer().<span class="function">new <span class="title">Inner</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure></p><h2 id="Java-中会存在内存泄漏吗，请简单描述。"><a href="#Java-中会存在内存泄漏吗，请简单描述。" class="headerlink" title="Java 中会存在内存泄漏吗，请简单描述。"></a>Java 中会存在内存泄漏吗，请简单描述。</h2><p>答：理论上 Java 因为有垃圾回收机制（GC）不会存在内存泄露问题（这也是 Java 被广泛使用于服务器端编程的一个重要原因）；然而在实际开发中，可能会存在无用但可达的对象，这些对象不能被 GC 回收，因此也会导致内存泄露的发生。例如 Hibernate 的 Session（一级缓存）中的对象属于持久态，垃圾回收器是不会回收这些对象的，然而这些对象中可能存在无用的垃圾对象，如果不及时关闭（close）或清空（flush）一级缓存就可能导致内存泄露。下面例子中的代码也会导致内存泄露。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"><span class="keyword">import</span> java.util.EmptyStackException;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyStack</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> T[] elements;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> size = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> INIT_CAPACITY = <span class="number">16</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyStack</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        elements = (T[]) <span class="keyword">new</span> Object[INIT_CAPACITY];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">push</span><span class="params">(T elem)</span> </span>&#123;</span><br><span class="line">        ensureCapacity();</span><br><span class="line">        elements[size++] = elem;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">pop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(size == <span class="number">0</span>) </span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> EmptyStackException();</span><br><span class="line">        <span class="keyword">return</span> elements[--size];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">ensureCapacity</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(elements.length == size) &#123;</span><br><span class="line">            elements = Arrays.copyOf(elements, <span class="number">2</span> * size + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>上面的代码实现了一个栈（先进后出（FILO））结构，乍看之下似乎没有什么明显的问题，它甚至可以通过你编写的各种单元测试。然而其中的 <code>pop</code> 方法却存在内存泄露的问题，当我们用 <code>pop</code> 方法弹出栈中的对象时，该对象不会被当作垃圾回收，即使使用栈的程序不再引用这些对象，因为栈内部维护着对这些对象的过期引用（obsolete reference）。在支持垃圾回收的语言中，内存泄露是很隐蔽的，这种内存泄露其实就是无意识的对象保持。如果一个对象引用被无意识的保留起来了，那么垃圾回收器不会处理这个对象，也不会处理该对象引用的其他对象，即使这样的对象只有少数几个，也可能会导致很多的对象被排除在垃圾回收之外，从而对性能造成重大影响，极端情况下会引发 Disk Paging（物理内存与硬盘的虚拟内存交换数据），甚至造成 OutOfMemoryError。</p><h2 id="抽象的（abstract）方法是否可同时是静态的（static），是否可同时是本地方法（native），是否可同时被-synchronized-修饰？"><a href="#抽象的（abstract）方法是否可同时是静态的（static），是否可同时是本地方法（native），是否可同时被-synchronized-修饰？" class="headerlink" title="抽象的（abstract）方法是否可同时是静态的（static），是否可同时是本地方法（native），是否可同时被 synchronized 修饰？"></a>抽象的（<code>abstract</code>）方法是否可同时是静态的（<code>static</code>），是否可同时是本地方法（<code>native</code>），是否可同时被 <code>synchronized</code> 修饰？</h2><p>答：都不能。抽象方法需要子类重写，而静态的方法是无法被重写的，因此二者是矛盾的。本地方法是由本地代码（如 C 代码）实现的方法，而抽象方法是没有实现的，也是矛盾的。<code>synchronized</code> 和方法的实现细节有关，抽象方法不涉及实现细节，因此也是相互矛盾的。</p><h2 id="阐述静态变量和实例变量的区别。"><a href="#阐述静态变量和实例变量的区别。" class="headerlink" title="阐述静态变量和实例变量的区别。"></a>阐述静态变量和实例变量的区别。</h2><p>答：静态变量是被 <code>static</code> 修饰符修饰的变量，也称为类变量，它属于类，不属于类的任何一个对象，一个类不管创建多少个对象，静态变量在内存中有且仅有一个拷贝；实例变量必须依存于某一实例，需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。</p><p><strong>补充：</strong>在 Java 开发中，上下文类和工具类中通常会有大量的静态成员。</p><h2 id="是否可以从一个静态（static）方法内部发出对非静态（non-static）方法的调用？"><a href="#是否可以从一个静态（static）方法内部发出对非静态（non-static）方法的调用？" class="headerlink" title="是否可以从一个静态（static）方法内部发出对非静态（non-static）方法的调用？"></a>是否可以从一个静态（static）方法内部发出对非静态（non-static）方法的调用？</h2><p>答：不可以，静态方法只能访问静态成员，因为非静态方法的调用要先创建对象，在调用静态方法时可能对象并没有被初始化。</p><h2 id="如何实现对象克隆？"><a href="#如何实现对象克隆？" class="headerlink" title="如何实现对象克隆？"></a>如何实现对象克隆？</h2><p>答：有两种方式：</p><ul><li>实现 <code>Cloneable</code> 接口并重写 <code>Object</code> 类中的 <code>clone()</code> 方法；</li><li>实现 <code>Serializable</code> 接口，通过对象的序列化和反序列化实现克隆，可以实现真正的深度克隆，代码如下。</li></ul><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.ByteArrayInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ByteArrayOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectInputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.ObjectOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.Serializable;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyUtil</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">MyUtil</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> AssertionError();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T extends Serializable&gt; <span class="function">T <span class="title">clone</span><span class="params">(T obj)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        ByteArrayOutputStream bout = <span class="keyword">new</span> ByteArrayOutputStream();</span><br><span class="line">        ObjectOutputStream oos = <span class="keyword">new</span> ObjectOutputStream(bout);</span><br><span class="line">        oos.writeObject(obj);</span><br><span class="line"></span><br><span class="line">        ByteArrayInputStream bin = <span class="keyword">new</span> ByteArrayInputStream(bout.toByteArray());</span><br><span class="line">        ObjectInputStream ois = <span class="keyword">new</span> ObjectInputStream(bin);</span><br><span class="line">        <span class="keyword">return</span> (T) ois.readObject();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 说明：调用 ByteArrayInputStream 或 ByteArrayOutputStream 对象的 close 方法没有任何意义</span></span><br><span class="line">        <span class="comment">// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源，这一点不同于对外部资源（如文件流）的释放</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>下面是测试代码：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.Serializable;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 人类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = -<span class="number">9102017020286042305L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String name;    <span class="comment">// 姓名</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> age;        <span class="comment">// 年龄</span></span><br><span class="line">    <span class="keyword">private</span> Car car;        <span class="comment">// 座驾</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Person</span><span class="params">(String name, <span class="keyword">int</span> age, Car car)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">        <span class="keyword">this</span>.car = car;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setName</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getAge</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setAge</span><span class="params">(<span class="keyword">int</span> age)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Car <span class="title">getCar</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> car;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setCar</span><span class="params">(Car car)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.car = car;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Person [name=&quot;</span> + name + <span class="string">&quot;, age=&quot;</span> + age + <span class="string">&quot;, car=&quot;</span> + car + <span class="string">&quot;]&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 小汽车类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Car</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = -<span class="number">5713945027627603702L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String brand;       <span class="comment">// 品牌</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> maxSpeed;       <span class="comment">// 最高时速</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Car</span><span class="params">(String brand, <span class="keyword">int</span> maxSpeed)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.brand = brand;</span><br><span class="line">        <span class="keyword">this</span>.maxSpeed = maxSpeed;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getBrand</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> brand;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBrand</span><span class="params">(String brand)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.brand = brand;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getMaxSpeed</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> maxSpeed;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setMaxSpeed</span><span class="params">(<span class="keyword">int</span> maxSpeed)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.maxSpeed = maxSpeed;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Car [brand=&quot;</span> + brand + <span class="string">&quot;, maxSpeed=&quot;</span> + maxSpeed + <span class="string">&quot;]&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CloneTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Person p1 = <span class="keyword">new</span> Person(<span class="string">&quot;Hao LUO&quot;</span>, <span class="number">33</span>, <span class="keyword">new</span> Car(<span class="string">&quot;Benz&quot;</span>, <span class="number">300</span>));</span><br><span class="line">            Person p2 = MyUtil.clone(p1); <span class="comment">// 深度克隆</span></span><br><span class="line">            p2.getCar().setBrand(<span class="string">&quot;BYD&quot;</span>);</span><br><span class="line">            <span class="comment">// 修改克隆的 Person 对象 p2 关联的汽车对象的品牌属性</span></span><br><span class="line">            <span class="comment">// 原来的 Person 对象 p1 关联的汽车不会受到任何影响</span></span><br><span class="line">            <span class="comment">// 因为在克隆 Person 对象时其关联的汽车对象也被克隆了</span></span><br><span class="line">            System.out.println(p1);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>注意：</strong>基于序列化和反序列化实现的克隆不仅仅是深度克隆，更重要的是通过泛型限定，可以检查出要克隆的对象是否支持序列化，这项检查是编译器完成的，不是在运行时抛出异常，这种是方案明显优于使用 <code>Object</code> 类的 <code>clone()</code> 方法克隆对象。让问题在编译的时候暴露出来总是好过把问题留到运行时。</p><h2 id="GC-是什么？为什么要有-GC？"><a href="#GC-是什么？为什么要有-GC？" class="headerlink" title="GC 是什么？为什么要有 GC？"></a>GC 是什么？为什么要有 GC？</h2><p>答：GC 是垃圾收集的意思，内存处理是编程人员容易出现问题的地方，忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃，Java 提供的 GC 功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的，Java 语言没有提供释放已分配内存的显示操作方法。Java 程序员不用担心内存管理，因为垃圾收集器会自动进行管理。要请求垃圾收集，可以调用下面的方法之一：<code>System.gc()</code> 或 <code>Runtime.getRuntime().gc()</code> ，但 JVM 可以屏蔽掉显示的垃圾回收调用。</p><p>垃圾回收可以有效的防止内存泄露，有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行，不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收，程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在 Java 诞生初期，垃圾回收是 Java 最大的亮点之一，因为服务器端的编程需要有效的防止内存泄露问题，然而时过境迁，如今 Java 的垃圾回收机制已经成为被诟病的东西。移动智能终端用户通常觉得 iOS 的系统比 Android 系统有更好的用户体验，其中一个深层次的原因就在于 Android 系统中垃圾回收的不可预知性。</p><p><strong>补充：</strong>垃圾回收机制有很多种，包括：分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的 Java 进程既有栈又有堆。栈保存了原始型局部变量，堆保存了要创建的对象。Java 平台对堆内存回收和再利用的基本算法被称为标记和清除，但是 Java 对其进行了改进，采用“分代式垃圾收集”。这种方法会跟 Java 对象的生命周期将堆内存划分为不同的区域，在垃圾收集过程中，可能会将对象移动到不同区域：</p><ul><li>伊甸园（Eden）：这是对象最初诞生的区域，并且对大多数对象来说，这里是它们唯一存在过的区域。</li><li>幸存者乐园（Survivor）：从伊甸园幸存下来的对象会被挪到这里。</li><li>终身颐养园（Tenured）：这是足够老的幸存对象的归宿。年轻代收集（Minor-GC）过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时，就会触发一次完全收集（Major-GC），这里可能还会牵扯到压缩，以便为大对象腾出足够的空间。</li></ul><p>与垃圾回收相关的 JVM 参数：</p><ul><li><code>-Xms / -Xmx</code>：堆的初始大小 / 堆的最大大小</li><li><code>-Xmn</code>：堆中年轻代的大小</li><li><code>-XX:-DisableExplicitGC</code>：让 System.gc() 不产生任何作用</li><li><code>-XX:+PrintGCDetails</code>：打印 GC 的细节</li><li><code>-XX:+PrintGCDateStamps</code>：打印 GC 操作的时间戳</li><li><code>-XX:NewSize / XX:MaxNewSize</code>：设置新生代大小/新生代最大大小</li><li><code>-XX:NewRatio</code>：可以设置老生代和新生代的比例</li><li><code>-XX:PrintTenuringDistribution</code>：设置每次新生代 GC 后输出幸存者乐园中对象年龄的分布</li><li><code>-XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold</code>：设置老年代阀值的初始值和最大值</li><li><code>-XX:TargetSurvivorRatio</code>：设置幸存区的目标使用率</li></ul><h2 id="String-s-new-String-quot-xyz-quot-创建了几个字符串对象？"><a href="#String-s-new-String-quot-xyz-quot-创建了几个字符串对象？" class="headerlink" title="String s = new String(&quot;xyz&quot;); 创建了几个字符串对象？"></a><code>String s = new String(&quot;xyz&quot;);</code> 创建了几个字符串对象？</h2><p>答：两个对象，一个是静态区的 <code>&quot;xyz&quot;</code>，一个是用 <code>new</code> 创建在堆上的对象。</p><h2 id="接口是否可继承（extends）接口？抽象类是否可实现（implements）接口？抽象类是否可继承具体类（concrete-class）？"><a href="#接口是否可继承（extends）接口？抽象类是否可实现（implements）接口？抽象类是否可继承具体类（concrete-class）？" class="headerlink" title="接口是否可继承（extends）接口？抽象类是否可实现（implements）接口？抽象类是否可继承具体类（concrete class）？"></a>接口是否可继承（extends）接口？抽象类是否可实现（implements）接口？抽象类是否可继承具体类（concrete class）？</h2><p>答：接口可以继承接口，而且支持多重继承。抽象类可以实现（implements）接口，抽象类可继承具体类也可以继承抽象类。</p><h2 id="一个-java-源文件中是否可以包含多个类（不是内部类）？有什么限制？"><a href="#一个-java-源文件中是否可以包含多个类（不是内部类）？有什么限制？" class="headerlink" title="一个 .java 源文件中是否可以包含多个类（不是内部类）？有什么限制？"></a>一个 <code>.java</code> 源文件中是否可以包含多个类（不是内部类）？有什么限制？</h2><p>答：可以，但一个源文件中最多只能有一个公开类（public class）而且文件名必须和公开类的类名完全保持一致。</p><h2 id="Anonymous-Inner-Class（匿名内部类）是否可以继承其它类？是否可以实现接口？"><a href="#Anonymous-Inner-Class（匿名内部类）是否可以继承其它类？是否可以实现接口？" class="headerlink" title="Anonymous Inner Class（匿名内部类）是否可以继承其它类？是否可以实现接口？"></a>Anonymous Inner Class（匿名内部类）是否可以继承其它类？是否可以实现接口？</h2><p>答：可以继承其他类或实现其他接口，在 Swing 编程和 Android 开发中常用此方式来实现事件监听和回调。</p><h2 id="内部类可以引用它的包含类（外部类）的成员吗？有没有什么限制？"><a href="#内部类可以引用它的包含类（外部类）的成员吗？有没有什么限制？" class="headerlink" title="内部类可以引用它的包含类（外部类）的成员吗？有没有什么限制？"></a>内部类可以引用它的包含类（外部类）的成员吗？有没有什么限制？</h2><p>答：一个内部类对象可以访问创建它的外部类对象的成员，包括私有成员。</p><h2 id="Java-中的-final-关键字有哪些用法？"><a href="#Java-中的-final-关键字有哪些用法？" class="headerlink" title="Java 中的 final 关键字有哪些用法？"></a>Java 中的 <code>final</code> 关键字有哪些用法？</h2><p>答：</p><ul><li>修饰类：表示该类不能被继承；</li><li>修饰方法：表示方法不能被重写；</li><li>修饰变量：表示变量只能一次赋值以后值不能被修改（常量）。</li></ul><h2 id="指出下面程序的运行结果。"><a href="#指出下面程序的运行结果。" class="headerlink" title="指出下面程序的运行结果。"></a>指出下面程序的运行结果。</h2><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.print(<span class="string">&quot;1&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">A</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.print(<span class="string">&quot;2&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">B</span> <span class="keyword">extends</span> <span class="title">A</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.print(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">B</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.print(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Hello</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        A ab = <span class="keyword">new</span> B();</span><br><span class="line">        ab = <span class="keyword">new</span> B();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>答：执行结果：<code>1a2b2b</code>。创建对象时构造器的调用顺序是：先初始化静态成员，然后调用父类构造器，再初始化非静态成员，最后调用自身构造器。</p><p><strong>注意：</strong>如果不能给出此题的正确答案，说明之前第 21 题 Java 类加载机制还没有完全理解，赶紧再看看吧。</p><h2 id="数据类型之间的转换："><a href="#数据类型之间的转换：" class="headerlink" title="数据类型之间的转换："></a>数据类型之间的转换：</h2><blockquote><ul><li>如何将字符串转换为基本数据类型？</li><li>如何将基本数据类型转换为字符串？</li></ul></blockquote><p>答：</p><ul><li>调用基本数据类型对应的包装类中的方法 <code>parseXXX(String)</code> 或 <code>valueOf(String)</code> 即可返回相应基本类型；</li><li>一种方法是将基本数据类型与空字符串（<code>&quot;&quot;</code>）连接（<code>+</code>）即可获得其所对应的字符串；另一种方法是调用 <code>String</code> 类中的 <code>valueOf()</code> 方法返回相应字符串</li></ul><h2 id="如何实现字符串的反转及替换？"><a href="#如何实现字符串的反转及替换？" class="headerlink" title="如何实现字符串的反转及替换？"></a>如何实现字符串的反转及替换？</h2><p>答：方法很多，可以自己写实现也可以使用 <code>String</code> 或 <code>StringBuffer/StringBuilder</code> 中的方法。有一道很常见的面试题是用递归实现字符串反转，代码如下所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">reverse</span><span class="params">(String originStr)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(originStr == <span class="keyword">null</span> || originStr.length() &lt;= <span class="number">1</span>) </span><br><span class="line">        <span class="keyword">return</span> originStr;</span><br><span class="line">    <span class="keyword">return</span> reverse(originStr.substring(<span class="number">1</span>)) + originStr.charAt(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="怎样将-GB2312-编码的字符串转换为-ISO-8859-1-编码的字符串？"><a href="#怎样将-GB2312-编码的字符串转换为-ISO-8859-1-编码的字符串？" class="headerlink" title="怎样将 GB2312 编码的字符串转换为 ISO-8859-1 编码的字符串？"></a>怎样将 GB2312 编码的字符串转换为 ISO-8859-1 编码的字符串？</h2><p>答：代码如下所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">String s1 = <span class="string">&quot;你好&quot;</span>;</span><br><span class="line">String s2 = <span class="keyword">new</span> String(s1.getBytes(<span class="string">&quot;GB2312&quot;</span>), <span class="string">&quot;ISO-8859-1&quot;</span>);</span><br></pre></td></tr></table></figure></p><h2 id="日期和时间："><a href="#日期和时间：" class="headerlink" title="日期和时间："></a>日期和时间：</h2><blockquote><p>问题 1：如何取得年月日、小时分钟秒？</p></blockquote><p>创建 <code>java.util.Calendar</code> 实例，调用其 <code>get()</code> 方法传入不同的参数即可获得参数所对应的值。Java 8 中可以使用 <code>java.time.LocalDateTime</code> 来获取，代码如下所示。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DateTimeTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Calendar cal = Calendar.getInstance();</span><br><span class="line">        System.out.println(cal.get(Calendar.YEAR));</span><br><span class="line">        System.out.println(cal.get(Calendar.MONTH));    <span class="comment">// 0 - 11</span></span><br><span class="line">        System.out.println(cal.get(Calendar.DATE));</span><br><span class="line">        System.out.println(cal.get(Calendar.HOUR_OF_DAY));</span><br><span class="line">        System.out.println(cal.get(Calendar.MINUTE));</span><br><span class="line">        System.out.println(cal.get(Calendar.SECOND));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Java 8</span></span><br><span class="line">        LocalDateTime dt = LocalDateTime.now();</span><br><span class="line">        System.out.println(dt.getYear());</span><br><span class="line">        System.out.println(dt.getMonthValue());     <span class="comment">// 1 - 12</span></span><br><span class="line">        System.out.println(dt.getDayOfMonth());</span><br><span class="line">        System.out.println(dt.getHour());</span><br><span class="line">        System.out.println(dt.getMinute());</span><br><span class="line">        System.out.println(dt.getSecond());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><blockquote><p>问题 2：如何取得从 1970 年 1 月 1 日 0 时 0 分 0 秒到现在的毫秒数？</p></blockquote><p>以下方法均可获得该毫秒数。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Calendar.getInstance().getTimeInMillis();</span><br><span class="line">System.currentTimeMillis();</span><br><span class="line">Clock.systemDefaultZone().millis(); <span class="comment">// Java 8</span></span><br></pre></td></tr></table></figure></p><blockquote><p>问题 3：如何取得某月的最后一天？</p></blockquote><p>代码如下所示。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Calendar time = Calendar.getInstance();</span><br><span class="line">time.getActualMaximum(Calendar.DAY_OF_MONTH);</span><br></pre></td></tr></table></figure></p><blockquote><p>问题 4：如何格式化日期？</p></blockquote><p>利用 <code>java.text.DataFormat</code> 的子类（如 <code>SimpleDateFormat</code> 类）中的 <code>format(Date)</code> 方法可将日期格式化。Java 8 中可以用 <code>java.time.format.DateTimeFormatter</code> 来格式化时间日期，代码如下所示。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.text.SimpleDateFormat;</span><br><span class="line"><span class="keyword">import</span> java.time.LocalDate;</span><br><span class="line"><span class="keyword">import</span> java.time.format.DateTimeFormatter;</span><br><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DateFormatTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        SimpleDateFormat oldFormatter = <span class="keyword">new</span> SimpleDateFormat(<span class="string">&quot;yyyy/MM/dd&quot;</span>);</span><br><span class="line">        Date date1 = <span class="keyword">new</span> Date();</span><br><span class="line">        System.out.println(oldFormatter.format(date1));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Java 8</span></span><br><span class="line">        DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern(<span class="string">&quot;yyyy/MM/dd&quot;</span>);</span><br><span class="line">        LocalDate date2 = LocalDate.now();</span><br><span class="line">        System.out.println(date2.format(newFormatter));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>补充：</strong>Java 的时间日期 API 一直以来都是被诟病的东西，为了解决这一问题，Java 8 中引入了新的时间日期 API，其中包括 <code>LocalDate</code>、<code>LocalTime</code>、<code>LocalDateTime</code>、<code>Clock</code>、<code>Instant</code> 等类，这些的类的设计都使用了不变模式，因此是线程安全的设计。如果不理解这些内容，可以参考我的另一篇文章<a href="https://blog.csdn.net/jackfrued/article/details/44499227">《关于 Java 并发编程的总结和思考》</a>。</p><h2 id="打印昨天的当前时刻。"><a href="#打印昨天的当前时刻。" class="headerlink" title="打印昨天的当前时刻。"></a>打印昨天的当前时刻。</h2><p>答：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Calendar;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">YesterdayCurrent</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span>&#123;</span><br><span class="line">        Calendar cal = Calendar.getInstance();</span><br><span class="line">        cal.add(Calendar.DATE, -<span class="number">1</span>);</span><br><span class="line">        System.out.println(cal.getTime());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>在 Java 8 中，可以用下面的代码实现相同的功能。</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.time.LocalDateTime;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">YesterdayCurrent</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        LocalDateTime today = LocalDateTime.now();</span><br><span class="line">        LocalDateTime yesterday = today.minusDays(<span class="number">1</span>);</span><br><span class="line">        System.out.println(yesterday);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="比较一下-Java-和-JavaSciprt。"><a href="#比较一下-Java-和-JavaSciprt。" class="headerlink" title="比较一下 Java 和 JavaSciprt。"></a>比较一下 Java 和 JavaSciprt。</h2><p>答：JavaScript 与 Java 是两个公司开发的不同的两个产品。Java 是原 Sun Microsystems 公司推出的面向对象的程序设计语言，特别适合于互联网应用程序开发；而 JavaScript 是 Netscape 公司的产品，为了扩展 Netscape 浏览器的功能而开发的一种可以嵌入 Web 页面中运行的基于对象和事件驱动的解释性语言。JavaScript 的前身是 LiveScript；而 Java 的前身是 Oak 语言。</p><p>下面对两种语言间的异同作如下比较：</p><ul><li>基于对象和面向对象：Java 是一种真正的面向对象的语言，即使是开发简单的程序，必须设计对象； JavaScript 是种脚本语言，它可以用来制作与网络无关的，与用户交互作用的复杂软件。它是一种基于对象（Object-Based）和事件驱动（Event-Driven）的编程语言，因而它本身提供了非常丰富的内部对象供设计人员使用。</li><li>解释和编译：Java 的源代码在执行之前，必须经过编译。JavaScript 是一种解释性编程语言，其源代码不需经过编译，由浏览器解释执行。（目前的浏览器几乎都使用了 JIT（即时编译）技术来提升 JavaScript 的运行效率）</li><li>强类型变量和类型弱变量：Java 采用强类型变量检查，即所有变量在编译之前必须作声明；JavaScript 中变量是弱类型的，甚至在使用变量前可以不作声明，JavaScript 的解释器在运行时检查推断其数据类型。</li><li>代码格式不一样。</li></ul><p><strong>补充：</strong>上面列出的四点是网上流传的所谓的标准答案。其实 Java 和 JavaScript 最重要的区别是一个是静态语言，一个是动态语言。目前的编程语言的发展趋势是函数式语言和动态语言。在 Java 中类（class）是一等公民，而 JavaScript 中函数（function）是一等公民，因此 JavaScript 支持函数式编程，可以使用 Lambda 函数和闭包（closure），当然 Java 8 也开始支持函数式编程，提供了对 Lambda 表达式以及函数式接口的支持。对于这类问题，在面试的时候最好还是用自己的语言回答会更加靠谱，不要背网上所谓的标准答案。</p><h2 id="什么时候用断言（assert）？"><a href="#什么时候用断言（assert）？" class="headerlink" title="什么时候用断言（assert）？"></a>什么时候用断言（assert）？</h2><p>答：断言在软件开发中是一种常用的调试方式，很多开发语言中都支持这种机制。一般来说，断言用于保证程序最基本、关键的正确性。断言检查通常在开发和测试时开启。为了保证程序的执行效率，在软件发布后断言检查通常是关闭的。断言是一个包含布尔表达式的语句，在执行这个语句时假定该表达式为 <code>true</code>；如果表达式的值为 <code>false</code>，那么系统会报告一个 AssertionError。断言的使用如下面的代码所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">assert</span>(a &gt; <span class="number">0</span>); <span class="comment">// throws an AssertionError if a &lt;= 0</span></span><br></pre></td></tr></table></figure></p><p>断言可以有两种形式：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">assert</span> Expression1;</span><br><span class="line"><span class="keyword">assert</span> Expression1 : Expression2 ;</span><br></pre></td></tr></table></figure></p><p>Expression1 应该总是产生一个布尔值。Expression2 可以是得出一个值的任意表达式；这个值用于生成显示更多调试信息的字符串消息。</p><p>要在运行时启用断言，可以在启动 JVM 时使用 <code>-enableassertions</code> 或者 <code>-ea</code> 标记。要在运行时选择禁用断言，可以在启动 JVM 时使用 <code>-da</code> 或者 <code>-disableassertions</code> 标记。要在系统类中启用或禁用断言，可使用 <code>-esa</code> 或 <code>-dsa</code> 标记。还可以在包的基础上启用或者禁用断言。</p><p><strong>注意：</strong>断言不应该以任何方式改变程序的状态。简单的说，如果希望在不满足某些条件时阻止代码的执行，就可以考虑用断言来阻止它。</p><h2 id="Error-和-Exception-有什么区别？"><a href="#Error-和-Exception-有什么区别？" class="headerlink" title="Error 和 Exception 有什么区别？"></a>Error 和 Exception 有什么区别？</h2><p>答：</p><ul><li>Error 表示系统级的错误和程序不必处理的异常，是恢复不是不可能但很困难的情况下的一种严重问题；比如内存溢出，不可能指望程序能处理这样的情况；</li><li>Exception 表示需要捕捉或者需要程序进行处理的异常，是一种设计或实现问题；也就是说，它表示如果程序运行正常，从不会发生的情况。</li></ul><p>面试题：2005 年摩托罗拉的面试中曾经问过这么一个问题“If a process reports a stack overflow run-time error, what’s the most possible cause?”，给了四个选项 a. lack of memory; b. write on an invalid memory space; c. recursive function calling; d. array index out of boundary. Java 程序在运行时也可能会遭遇 StackOverflowError，这是一个无法恢复的错误，只能重新修改代码了，这个面试题的答案是 c。如果写了不能迅速收敛的递归，则很有可能引发栈溢出的错误，如下所示：</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StackOverflowErrorTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        main(<span class="keyword">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>递归编写<strong>注意：</strong>用程序时一定要牢记两点：1. 递归公式；2. 收敛条件（什么时候就不再继续递归）。</p><h2 id="try-里有一个-return-语句，那么紧跟在这个-try-后的-finally-里的代码会不会被执行，什么时候被执行，在-return-前还是后"><a href="#try-里有一个-return-语句，那么紧跟在这个-try-后的-finally-里的代码会不会被执行，什么时候被执行，在-return-前还是后" class="headerlink" title="try{} 里有一个 return 语句，那么紧跟在这个 try 后的 finally{} 里的代码会不会被执行，什么时候被执行，在 return 前还是后?"></a><code>try&#123;&#125;</code> 里有一个 <code>return</code> 语句，那么紧跟在这个 <code>try</code> 后的 <code>finally&#123;&#125;</code> 里的代码会不会被执行，什么时候被执行，在 <code>return</code> 前还是后?</h2><p>答：会执行，在方法返回调用者前执行。</p><p><strong>注意：</strong>在 <code>finally</code> 中改变返回值的做法是不好的，因为如果存在 <code>finally</code> 代码块，<code>try</code> 中的 <code>return</code> 语句不会立马返回调用者，而是记录下返回值待 <code>finally</code> 代码块执行完毕之后再向调用者返回其值，然后如果在 <code>finally</code> 中修改了返回值，就会返回修改后的值。显然，在 <code>finally</code> 中返回或者修改返回值会对程序造成很大的困扰，C# 中直接用编译错误的方式来阻止程序员干这种龌龊的事情，Java 中也可以通过提升编译器的语法检查级别来产生警告或错误，Eclipse 中可以在如图所示的地方进行设置，强烈建议将此项设置为编译错误。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/java_interview/46.png" alt="编译器的语法检查级别"></p><h2 id="Java-语言如何进行异常处理，关键字：throws、throw、try、catch、finally-分别如何使用？"><a href="#Java-语言如何进行异常处理，关键字：throws、throw、try、catch、finally-分别如何使用？" class="headerlink" title="Java 语言如何进行异常处理，关键字：throws、throw、try、catch、finally 分别如何使用？"></a>Java 语言如何进行异常处理，关键字：<code>throws</code>、<code>throw</code>、<code>try</code>、<code>catch</code>、<code>finally</code> 分别如何使用？</h2><p>答：Java 通过面向对象的方法进行异常处理，把各种不同的异常进行分类，并提供了良好的接口。在 Java 中，每个异常都是一个对象，它是 <code>Throwable</code> 类或其子类的实例。当一个方法出现异常后便抛出一个异常对象，该对象中包含有异常信息，调用这个对象的方法可以捕获到这个异常并可以对其进行处理。</p><p>Java 的异常处理是通过 5 个关键词来实现的：<code>try</code>、<code>catch</code>、<code>throw</code>、<code>throws</code> 和 <code>finally</code>。</p><p>一般情况下是用 <code>try</code> 来执行一段程序，如果系统会抛出（throw）一个异常对象，可以通过它的类型来捕获（catch）它，或通过总是执行代码块（finally）来处理；<code>try</code> 用来指定一块预防所有异常的程序；<code>catch</code> 子句紧跟在 <code>try</code> 块后面，用来指定你想要捕获的异常的类型；<code>throw</code> 语句用来明确地抛出一个异常；<code>throws</code> 用来声明一个方法可能抛出的各种异常（当然声明异常时允许无病呻吟）；<code>finally</code> 为确保一段代码不管发生什么异常状况都要被执行；<code>try</code> 语句可以嵌套，每当遇到一个 <code>try</code> 语句，异常的结构就会被放入异常栈中，直到所有的 <code>try</code> 语句都完成。如果下一级的 <code>try</code> 语句没有对某种异常进行处理，异常栈就会执行出栈操作，直到遇到有处理这种异常的 <code>try</code> 语句或者最终将异常抛给 JVM。</p><h2 id="运行时异常与受检异常有何异同？"><a href="#运行时异常与受检异常有何异同？" class="headerlink" title="运行时异常与受检异常有何异同？"></a>运行时异常与受检异常有何异同？</h2><p>答：异常表示程序运行过程中可能出现的非正常状态，运行时异常表示虚拟机的通常操作中可能遇到的异常，是一种常见运行错误，只要程序设计得没有问题通常就不会发生。受检异常跟程序运行的上下文环境有关，即使程序设计无误，仍然可能因使用的问题而引发。Java 编译器要求方法必须声明抛出可能发生的受检异常，但是并不要求必须声明抛出未被捕获的运行时异常。异常和继承一样，是面向对象程序设计中经常被滥用的东西，在《Effective Java》中对异常的使用给出了以下指导原则：</p><ul><li>不要将异常处理用于正常的控制流（设计良好的 API 不应该强迫它的调用者为了正常的控制流而使用异常）</li><li>对可以恢复的情况使用受检异常，对编程错误使用运行时异常</li><li>避免不必要的使用受检异常（可以通过一些状态检测手段来避免异常的发生）</li><li>优先使用标准的异常</li><li>每个方法抛出的异常都要有文档</li><li>保持异常的原子性</li><li>不要在 <code>catch</code> 中忽略掉捕获到的异常</li></ul><h2 id="列出一些你常见的运行时异常？"><a href="#列出一些你常见的运行时异常？" class="headerlink" title="列出一些你常见的运行时异常？"></a>列出一些你常见的运行时异常？</h2><p>答：</p><ul><li><code>ArithmeticException</code>：算术异常</li><li><code>ClassCastException</code>：类转换异常</li><li><code>IllegalArgumentException</code>：非法参数异常</li><li><code>IndexOutOfBoundsException</code>：下标越界异常</li><li><code>NullPointerException</code>：空指针异常</li><li><code>SecurityException</code>：安全异常</li></ul><h2 id="阐述-final、finally、finalize-的区别。"><a href="#阐述-final、finally、finalize-的区别。" class="headerlink" title="阐述 final、finally、finalize 的区别。"></a>阐述 <code>final</code>、<code>finally</code>、<code>finalize</code> 的区别。</h2><p>答：</p><ul><li><code>final</code>：修饰符（关键字）有三种用法：如果一个类被声明为 <code>final</code>，意味着它不能再派生出新的子类，即不能被继承，因此它和 <code>abstract</code> 是反义词。将变量声明为 <code>final</code>，可以保证它们在使用中不被改变，被声明为 <code>final</code> 的变量必须在声明时给定初值，而在以后的引用中只能读取不可修改。被声明为 <code>final</code> 的方法也同样只能使用，不能在子类中被重写。</li><li><code>finally</code>：通常放在 <code>try…catch…</code> 的后面构造总是执行代码块，这就意味着程序无论正常执行还是发生异常，这里的代码只要 JVM 不关闭都能执行，可以将释放外部资源的代码写在 <code>finally</code> 块中。</li><li><code>finalize</code>：<code>Object</code> 类中定义的方法，Java 中允许使用 <code>finalize()</code> 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的，通过重写 <code>finalize()</code> 方法可以整理系统资源或者执行其他清理工作。</li></ul><h2 id="类-ExampleA-继承-Exception，类-ExampleB-继承-ExampleA。有如下代码片段："><a href="#类-ExampleA-继承-Exception，类-ExampleB-继承-ExampleA。有如下代码片段：" class="headerlink" title="类 ExampleA 继承 Exception，类 ExampleB 继承 ExampleA。有如下代码片段："></a>类 <code>ExampleA</code> 继承 <code>Exception</code>，类 <code>ExampleB</code> 继承 <code>ExampleA</code>。有如下代码片段：</h2><blockquote><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> ExampleB(<span class="string">&quot;b&quot;</span>)</span><br><span class="line">&#125; <span class="keyword">catch</span>(ExampleA e)&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;ExampleA&quot;</span>);</span><br><span class="line">&#125; <span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;Exception&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>请问执行此段代码的输出是什么？</p></blockquote><p>答：输出：<code>ExampleA</code>。根据里氏代换原则（能使用父类型的地方一定能使用子类型），抓取<code>ExampleA</code> 类型异常的 <code>catch</code> 块能够抓住 <code>try</code> 块中抛出的 <code>ExampleB</code> 类型的异常。</p><p>面试题 - 说出下面代码的运行结果。（此题的出处是《Java 编程思想》一书）</p><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Annoyance</span> <span class="keyword">extends</span> <span class="title">Exception</span> </span>&#123;&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sneeze</span> <span class="keyword">extends</span> <span class="title">Annoyance</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Human</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> Sneeze();</span><br><span class="line">            &#125; </span><br><span class="line">            <span class="keyword">catch</span> ( Annoyance a ) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;Caught Annoyance&quot;</span>);</span><br><span class="line">                <span class="keyword">throw</span> a;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; </span><br><span class="line">        <span class="keyword">catch</span> ( Sneeze s ) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;Caught Sneeze&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">finally</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;Hello World!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">//       输出结果：</span></span><br><span class="line"><span class="comment">//       Caught Annoyance</span></span><br><span class="line"><span class="comment">//       Caught Sneeze</span></span><br><span class="line"><span class="comment">//       Hello World!</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Java/">Java</category>
      
      
      <comments>https://blog.eurkon.com/post/11ea51ea.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Java 面试题解析</title>
      <link>https://blog.eurkon.com/post/d2aef718.html</link>
      <guid>https://blog.eurkon.com/post/d2aef718.html</guid>
      <pubDate>Mon, 15 Mar 2021 01:00:00 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Java-基础&quot;&gt;&lt;a href=&quot;#Java-基础&quot; class=&quot;headerlink&quot; title=&quot;Java 基础&quot;&gt;&lt;/a&gt;&lt;a href=&quot;/post/11ea51ea.html&quot;&gt;Java 基础&lt;/a&gt;&lt;/h2&gt;&lt;h2 id=&quot;Java-容器&quot;&gt;&lt;a</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Java-基础"><a href="#Java-基础" class="headerlink" title="Java 基础"></a><a href="/post/11ea51ea.html">Java 基础</a></h2><h2 id="Java-容器"><a href="#Java-容器" class="headerlink" title="Java 容器"></a><a href="/post/d07acd09.html">Java 容器</a></h2><h2 id="Java-并发"><a href="#Java-并发" class="headerlink" title="Java 并发"></a><a href="/post/32cf50db.html">Java 并发</a></h2><h2 id="Java-IO流"><a href="#Java-IO流" class="headerlink" title="Java IO流"></a><a href="/post/af128a42.html">Java IO流</a></h2><h2 id="Java-数据库"><a href="#Java-数据库" class="headerlink" title="Java 数据库"></a><a href="/post/73adfcb0.html">Java 数据库</a></h2><h2 id="Java-反射"><a href="#Java-反射" class="headerlink" title="Java 反射"></a><a href="/post/9fbf7373.html">Java 反射</a></h2><h2 id="Java-设计模式"><a href="#Java-设计模式" class="headerlink" title="Java 设计模式"></a><a href="/post/1cfbd3b3.html">Java 设计模式</a></h2><h2 id="Java-Web"><a href="#Java-Web" class="headerlink" title="Java Web"></a><a href="/post/d24a315e.html">Java Web</a></h2><h2 id="Java-EE"><a href="#Java-EE" class="headerlink" title="Java EE"></a><a href="/post/dc04fa32.html">Java EE</a></h2>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%9D%A2%E8%AF%95%E7%B3%BB%E5%88%97/">面试系列</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Java/">Java</category>
      
      
      <comments>https://blog.eurkon.com/post/d2aef718.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>排序算法</title>
      <link>https://blog.eurkon.com/post/735e5788.html</link>
      <guid>https://blog.eurkon.com/post/735e5788.html</guid>
      <pubDate>Sat, 13 Mar 2021 05:16:02 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;排序算法&quot;&gt;&lt;a href=&quot;#排序算法&quot; class=&quot;headerlink&quot; title=&quot;排序算法&quot;&gt;&lt;/a&gt;排序算法&lt;/h2&gt;&lt;p&gt;排序是计算机程序设计中的一种重要操作，它的功能是将一个数据元素（或记录）的任意序列，重新排列成一个关键字有序的序列，排序就是</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="排序算法"><a href="#排序算法" class="headerlink" title="排序算法"></a>排序算法</h2><p>排序是计算机程序设计中的一种重要操作，它的功能是将一个数据元素（或记录）的任意序列，重新排列成一个关键字有序的序列，排序就是把集合中的元素按照一定的次序排序在一起。</p><p>排序算法可以分为内部排序和外部排序，内部排序是数据记录在内存中进行排序，而外部排序是因排序的数据很大，一次不能容纳全部的排序记录，在排序过程中需要访问外存。常见的内部排序算法有：插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。</p><h3 id="相关概念"><a href="#相关概念" class="headerlink" title="相关概念"></a>相关概念</h3><ul><li>稳定排序：如果 a 原本在 b 的前面，且 a == b，排序之后 a 仍然在 b 的前面，则为稳定排序。</li><li>非稳定排序：如果 a 原本在 b 的前面，且 a == b，排序之后 a 可能不在 b 的前面，则为非稳定排序。</li><li>原地排序：原地排序就是指在排序过程中不申请多余的存储空间，只利用原来存储待排数据的存储空间进行比较和交换的数据排序。</li><li>非原地排序：需要利用额外的数组来辅助排序。</li><li>时间复杂度：一个算法执行所消耗的时间。</li><li>空间复杂度：运行完一个算法所需的内存大小。</li></ul><h3 id="时间复杂度对比"><a href="#时间复杂度对比" class="headerlink" title="时间复杂度对比"></a>时间复杂度对比</h3><div class="table-container"><table><thead><tr><th>排序算法</th><th>平均时间复杂度</th><th>最坏时间复杂度</th><th>最优时间复杂度</th><th>空间复杂度</th><th>排序方式</th><th>稳定性</th></tr></thead><tbody><tr><td><a href="#冒泡排序">冒泡排序</a></td><td>O(n²)</td><td>O(n²)</td><td>O(n)</td><td>O(1)</td><td>In-Place</td><td>稳定</td></tr><tr><td><a href="#选择排序">选择排序</a></td><td>O(n²)</td><td>O(n²)</td><td>O(n²)</td><td>O(1)</td><td>In-Place</td><td>不稳定</td></tr><tr><td><a href="#插入排序">插入排序</a></td><td>O(n²)</td><td>O(n²)</td><td>O(n)</td><td>O(1)</td><td>In-Place</td><td>稳定</td></tr><tr><td><a href="#希尔排序">希尔排序</a></td><td>O(n¹˙³)</td><td>O(n²)</td><td>O(n)</td><td>O(1)</td><td>In-Place</td><td>不稳定</td></tr><tr><td><a href="#归并排序">归并排序</a></td><td>O(nlogn)</td><td>O(nlogn)</td><td>O(nlogn)</td><td>O(n)</td><td>Out-Place</td><td>稳定</td></tr><tr><td><a href="#快速排序">快速排序</a></td><td>O(nlogn)</td><td>O(n²)</td><td>O(nlogn)</td><td>O(logn)</td><td>In-Place</td><td>不稳定</td></tr><tr><td><a href="#堆排序">堆排序</a></td><td>O(nlogn)</td><td>O(nlogn)</td><td>O(nlogn)</td><td>O(1)</td><td>In-Place</td><td>不稳定</td></tr><tr><td><a href="#计数排序">计数排序</a></td><td>O(n+k)</td><td>O(n+k)</td><td>O(n+k)</td><td>O(k)</td><td>Out-Place</td><td>稳定</td></tr><tr><td><a href="#桶排序">桶排序</a></td><td>O(n+k)</td><td>O(n²)</td><td>O(n)</td><td>O(n+k)</td><td>Out-Place</td><td>稳定</td></tr><tr><td><a href="#基数排序">基数排序</a></td><td>O(n×k)</td><td>O(n×k)</td><td>O(n×k)</td><td>O(n+k)</td><td>Out-Place</td><td>稳定</td></tr></tbody></table></div><p><strong>注意：</strong></p><ul><li>n：数据规模</li><li>k：&quot;桶&quot;的个数</li><li>In-place：占用常数内存，不占用额外内存</li><li>Out-place：占用额外内存</li><li>稳定性：排序后 2 个相等键值的顺序和排序之前它们的顺序相同</li></ul><h2 id="冒泡排序"><a href="#冒泡排序" class="headerlink" title="冒泡排序"></a>冒泡排序</h2><p>冒泡排序（Bubble Sort）又称为泡式排序，是一种简单的排序算法。它重复地走访过要排序的数列，一次比较两个元素，如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换，也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。</p><p>冒泡排序对 n 个项目需要 O(n²) 的比较次数，且可以原地排序。尽管这个算法是最简单了解和实现的排序算法之一，但它对于包含大量的元素的数列排序是很没有效率的。</p><h3 id="算法步骤"><a href="#算法步骤" class="headerlink" title="算法步骤"></a>算法步骤</h3><ol><li>比较相邻的元素。如果第一个比第二个大，就交换他们两个；</li><li>对每一对相邻元素作同样的工作，从开始第一对到结尾的最后一对。这步做完后，最后的元素会是最大的；</li><li>针对所有的元素重复以上的步骤，除了最后一个；</li><li>持续每次对越来越少的元素重复上面的步骤，直到没有任何一对数字需要比较。</li></ol><h3 id="动图展示"><a href="#动图展示" class="headerlink" title="动图展示"></a>动图展示</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/sort_algorithm/bubble_sort.gif" alt="冒泡排序"></p><h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Java"><a href="#Java" class="headerlink" title="Java"></a>Java</h4><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">int</span>[] bubbleSort(<span class="keyword">int</span>[] array) &#123;</span><br><span class="line">    <span class="keyword">int</span> temp;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; array.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">boolean</span> Flag = <span class="keyword">false</span>; <span class="comment">// 是否发生交换。没有交换，提前跳出外层循环</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; array.length - <span class="number">1</span> - i; j++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (array[j] &gt; array[j + <span class="number">1</span>]) &#123;</span><br><span class="line">                temp = array[j];</span><br><span class="line">                array[j] = array[j + <span class="number">1</span>];</span><br><span class="line">                array[j + <span class="number">1</span>] = temp;</span><br><span class="line">                Flag = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!Flag)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> array;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Python"><a href="#Python" class="headerlink" title="Python"></a>Python</h4><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">bubble_sort</span>(<span class="params">iterable</span>):</span></span><br><span class="line">    new_arr = arr(iterable)</span><br><span class="line">    arr_len = <span class="built_in">len</span>(new_arr)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(arr_len):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(arr_len - i - <span class="number">1</span>):</span><br><span class="line">            <span class="keyword">if</span> new_arr[j] &gt; new_arr[j + <span class="number">1</span>]:</span><br><span class="line">                new_arr[j], new_arr[j + <span class="number">1</span>] = new_arr[j + <span class="number">1</span>], new_arr[j]</span><br><span class="line">    <span class="keyword">return</span> new_arr</span><br></pre></td></tr></table></figure></p><h2 id="选择排序"><a href="#选择排序" class="headerlink" title="选择排序"></a>选择排序</h2><p>选择排序（Selection sort）是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小（大）元素，存放到排序序列的起始位置，然后，再从剩余未排序元素中继续寻找最小（大）元素，然后放到已排序序列的末尾。以此类推，直到所有元素均排序完毕。</p><p>选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上，则它不会被移动。选择排序每次交换一对元素，它们当中至少有一个将被移到其最终位置上，因此对 n 个元素的表进行排序总共进行至多 (n-1) 次交换。在所有的完全依靠交换去移动元素的排序方法中，选择排序属于非常好的一种。</p><h3 id="算法步骤-1"><a href="#算法步骤-1" class="headerlink" title="算法步骤"></a>算法步骤</h3><ol><li>首先在未排序序列中找到最小（大）元素，存放到排序序列的起始位置；</li><li>再从剩余未排序元素中继续寻找最小（大）元素，然后放到已排序序列的末尾；</li><li>重复第二步，直到所有元素均排序完毕。</li></ol><h3 id="动图展示-1"><a href="#动图展示-1" class="headerlink" title="动图展示"></a>动图展示</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/sort_algorithm/selection_sort.gif" alt="选择排序"></p><h3 id="代码实现-1"><a href="#代码实现-1" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Java-1"><a href="#Java-1" class="headerlink" title="Java"></a>Java</h4><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">int</span>[] selectionSort(<span class="keyword">int</span>[] arr) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="comment">// 总共要经过 n-1 轮比较</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; arr.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">int</span> min = i;</span><br><span class="line">        <span class="comment">// 每轮需要比较的次数 N-i</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = i + <span class="number">1</span>; j &lt; arr.length; j++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (arr[j] &lt; arr[min]) &#123;</span><br><span class="line">                <span class="comment">// 记录目前能找到的最小值元素的下标</span></span><br><span class="line">                min = j;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 将找到的最小值和 i 位置所在的值进行交换</span></span><br><span class="line">        <span class="keyword">if</span> (i != min) &#123;</span><br><span class="line">            <span class="keyword">int</span> tmp = arr[i];</span><br><span class="line">            arr[i] = arr[min];</span><br><span class="line">            arr[min] = tmp;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> arr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Python-1"><a href="#Python-1" class="headerlink" title="Python"></a>Python</h4><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">selection_sort</span>(<span class="params">arr</span>):</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(arr)-<span class="number">1</span>):</span><br><span class="line">        minIndex = i</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(i + <span class="number">1</span>, <span class="built_in">len</span>(arr)):</span><br><span class="line">            <span class="keyword">if</span> arr[minIndex] &gt; arr[j]:</span><br><span class="line">                minIndex = j</span><br><span class="line">        <span class="keyword">if</span> i == minIndex:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            arr[i], arr[minIndex] = arr[minIndex], arr[i]</span><br><span class="line">    <span class="keyword">return</span> arr</span><br></pre></td></tr></table></figure></p><h2 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h2><p>插入排序（Insertion Sort）是一种简单直观的排序算法。它的工作原理是通过构建有序序列，对于未排序数据，在已排序序列中从后向前扫描，找到相应位置并插入。插入排序在实现上，通常采用 In-Place 排序（即只需用到 O(1) 的额外空间的排序），因而在从后向前扫描过程中，需要反复把已排序元素逐步向后挪位，为最新元素提供插入空间。</p><h3 id="算法步骤-2"><a href="#算法步骤-2" class="headerlink" title="算法步骤"></a>算法步骤</h3><ol><li>从第一个元素开始，该元素可以认为已经被排序；</li><li>取出下一个元素，在已经排序的元素序列中从后向前扫描；</li><li>如果该元素（已排序）大于新元素，将该元素移到下一位置；</li><li>重复步骤 3，直到找到已排序的元素小于或者等于新元素的位置；</li><li>将新元素插入到该位置后；</li><li>重复步骤 2~5。</li></ol><h3 id="动图展示-2"><a href="#动图展示-2" class="headerlink" title="动图展示"></a>动图展示</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/sort_algorithm/insertion_sort.gif" alt="插入排序"></p><h3 id="代码实现-2"><a href="#代码实现-2" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Java-2"><a href="#Java-2" class="headerlink" title="Java"></a>Java</h4><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">int</span>[] insertionSort(<span class="keyword">int</span>[] arr) <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="comment">// 从下标为 1 的元素开始选择合适的位置插入，因为下标为 0 的元素，默认是有序的</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">        <span class="comment">// 记录要插入的数据</span></span><br><span class="line">        <span class="keyword">int</span> tmp = arr[i];</span><br><span class="line">        <span class="comment">// 从已经排序的序列最右边的开始比较，找到比其小的数</span></span><br><span class="line">        <span class="keyword">int</span> j = i;</span><br><span class="line">        <span class="keyword">while</span> (j &gt; <span class="number">0</span> &amp;&amp; tmp &lt; arr[j - <span class="number">1</span>]) &#123;</span><br><span class="line">            arr[j] = arr[j - <span class="number">1</span>];</span><br><span class="line">            j--;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 存在比其小的数，插入</span></span><br><span class="line">        <span class="keyword">if</span> (j != i) &#123;</span><br><span class="line">            arr[j] = tmp;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> arr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Python-2"><a href="#Python-2" class="headerlink" title="Python"></a>Python</h4><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">insertion_sort</span>(<span class="params">arr</span>):</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(arr)):</span><br><span class="line">        preIndex = i - <span class="number">1</span></span><br><span class="line">        current = arr[i]</span><br><span class="line">        <span class="keyword">while</span> preIndex &gt;= <span class="number">0</span> <span class="keyword">and</span> arr[preIndex] &gt; current:</span><br><span class="line">            arr[preIndex + <span class="number">1</span>] = arr[preIndex]</span><br><span class="line">            preIndex -= <span class="number">1</span></span><br><span class="line">        arr[preIndex + <span class="number">1</span>] = current</span><br><span class="line">    <span class="keyword">return</span> arr</span><br></pre></td></tr></table></figure></p><h2 id="希尔排序"><a href="#希尔排序" class="headerlink" title="希尔排序"></a>希尔排序</h2><p>希尔排序（Shellsort），也称递减增量排序算法，是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。</p><p>希尔排序是基于插入排序的以下两点性质而提出改进方法的：</p><ul><li>插入排序在对几乎已经排好序的数据操作时，效率高，即可以达到线性排序的效率</li><li>但插入排序一般来说是低效的，因为插入排序每次只能将数据移动一位</li></ul><p>希尔排序的基本思想是：先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序，待整个序列中的记录&quot;基本有序&quot;时，再对全体记录进行依次直接插入排序。</p><h3 id="算法步骤-3"><a href="#算法步骤-3" class="headerlink" title="算法步骤"></a>算法步骤</h3><ol><li>选择一个增量序列 t₁, t₂, ..., tₖ，其中 tᵢ &gt; tⱼ, tₖ = 1；</li><li>按增量序列个数 k，对序列进行 k 趟排序；</li><li>每趟排序，根据对应的增量 tᵢ，将待排序列分割成若干长度为 m 的子序列，分别对各子表进行直接插入排序。仅增量因子为 1 时，整个序列作为一个表来处理，表长度即为整个序列的长度。</li></ol><h3 id="动图展示-3"><a href="#动图展示-3" class="headerlink" title="动图展示"></a>动图展示</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/sort_algorithm/shell_sort.gif" alt="希尔排序"></p><h3 id="代码实现-3"><a href="#代码实现-3" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Java-3"><a href="#Java-3" class="headerlink" title="Java"></a>Java</h4><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">shellSort</span><span class="params">(<span class="keyword">int</span>[] arr)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> length = arr.length;</span><br><span class="line">    <span class="keyword">int</span> temp;</span><br><span class="line">    <span class="comment">// 动态定义间隔序列</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> step = length / <span class="number">2</span>; step &gt;= <span class="number">1</span>; step /= <span class="number">2</span>) &#123;</span><br><span class="line">        <span class="comment">// 插入排序</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = step; i &lt; length; i++) &#123;</span><br><span class="line">            temp = arr[i];</span><br><span class="line">            <span class="keyword">int</span> j = i - step;</span><br><span class="line">            <span class="keyword">while</span> (j &gt;= <span class="number">0</span> &amp;&amp; arr[j] &gt; temp) &#123;</span><br><span class="line">                arr[j + step] = arr[j];</span><br><span class="line">                j -= step;</span><br><span class="line">            &#125;</span><br><span class="line">            arr[j + step] = temp;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Python-3"><a href="#Python-3" class="headerlink" title="Python"></a>Python</h4><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">shell_sort</span>(<span class="params">arr</span>):</span></span><br><span class="line">    n = <span class="built_in">len</span>(arr)</span><br><span class="line">    <span class="comment"># 初始步長</span></span><br><span class="line">    gap = n // <span class="number">2</span></span><br><span class="line">    <span class="keyword">while</span> gap &gt; <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(gap, n):</span><br><span class="line">            <span class="comment"># 每个步長進行插入排序</span></span><br><span class="line">            temp = arr[i]</span><br><span class="line">            j = i</span><br><span class="line">            <span class="comment"># 插入排序</span></span><br><span class="line">            <span class="keyword">while</span> j &gt;= <span class="number">0</span> <span class="keyword">and</span> j-gap &gt;= <span class="number">0</span> <span class="keyword">and</span> arr[j - gap] &gt; temp:</span><br><span class="line">                arr[j] = arr[j - gap]</span><br><span class="line">                j -= gap</span><br><span class="line">            arr[j] = temp</span><br><span class="line">        <span class="comment"># 得到新的步長</span></span><br><span class="line">        gap = gap // <span class="number">2</span></span><br><span class="line">    <span class="keyword">return</span> arr</span><br></pre></td></tr></table></figure></p><h2 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h2><p>归并排序（Merge sort）是建立在归并操作上的一种有效的排序算法。该算法是采用分治法（Divide and Conquer）的一个非常典型的应用。</p><p>作为一种典型的分而治之思想的算法应用，归并排序的实现由两种方法：</p><p>自上而下的递归（所有递归的方法都可以用迭代重写，所以就有了第 2 种方法）；自下而上的迭代；</p><h3 id="算法步骤-4"><a href="#算法步骤-4" class="headerlink" title="算法步骤"></a>算法步骤</h3><p><strong>递归法（Top-down）</strong></p><ol><li>申请空间，使其大小为两个已经排序序列之和，该空间用来存放合并后的序列；</li><li>设定两个指针，最初位置分别为两个已经排序序列的起始位置；</li><li>比较两个指针所指向的元素，选择相对小的元素放入到合并空间，并移动指针到下一位置；</li><li>重复步骤 3 直到某一指针到达序列尾；</li><li>将另一序列剩下的所有元素直接复制到合并序列尾。</li></ol><p><strong>迭代法（Bottom-up）</strong></p><p>原理如下（假设序列共有 n 个元素）：</p><ol><li>将序列每相邻两个数字进行归并操作，形成 ceil(n/2) 个序列，排序后每个序列包含两/一个元素；</li><li>若此时序列数不是 1 个则将上述序列再次归并，形成 ceil(n/4) 个序列，每个序列包含四/三个元素；</li><li>重复步骤 2，直到所有元素排序完毕，即序列数为 1。</li></ol><h3 id="动图展示-4"><a href="#动图展示-4" class="headerlink" title="动图展示"></a>动图展示</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/sort_algorithm/merge_sort.gif" alt="归并排序"></p><h3 id="代码实现-4"><a href="#代码实现-4" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Java-4"><a href="#Java-4" class="headerlink" title="Java"></a>Java</h4><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 递归版：</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">merge_sort_recursive</span><span class="params">(<span class="keyword">int</span>[] arr, <span class="keyword">int</span>[] result, <span class="keyword">int</span> start, <span class="keyword">int</span> end)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (start &gt;= end)</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">int</span> len = end - start, mid = (len &gt;&gt; <span class="number">1</span>) + start;</span><br><span class="line">    <span class="keyword">int</span> start1 = start, end1 = mid;</span><br><span class="line">    <span class="keyword">int</span> start2 = mid + <span class="number">1</span>, end2 = end;</span><br><span class="line">    merge_sort_recursive(arr, result, start1, end1); <span class="comment">//左边归并排序，使得左子序列有序</span></span><br><span class="line">    merge_sort_recursive(arr, result, start2, end2); <span class="comment">//右边归并排序，使得右子序列有序</span></span><br><span class="line">    <span class="keyword">int</span> k = start;</span><br><span class="line">    <span class="keyword">while</span> (start1 &lt;= end1 &amp;&amp; start2 &lt;= end2)</span><br><span class="line">        result[k++] = arr[start1] &lt; arr[start2] ? arr[start1++] : arr[start2++];</span><br><span class="line">    <span class="keyword">while</span> (start1 &lt;= end1)</span><br><span class="line">        result[k++] = arr[start1++];</span><br><span class="line">    <span class="keyword">while</span> (start2 &lt;= end2)</span><br><span class="line">        result[k++] = arr[start2++];</span><br><span class="line">    <span class="keyword">for</span> (k = start; k &lt;= end; k++)</span><br><span class="line">        arr[k] = result[k];</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">merge_sort</span><span class="params">(<span class="keyword">int</span>[] arr)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> len = arr.length;</span><br><span class="line">    <span class="keyword">int</span>[] result = <span class="keyword">new</span> <span class="keyword">int</span>[len];</span><br><span class="line">    merge_sort_recursive(arr, result, <span class="number">0</span>, len - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 迭代版：</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">merge_sort</span><span class="params">(<span class="keyword">int</span>[] arr)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span>[] orderedArr = <span class="keyword">new</span> <span class="keyword">int</span>[arr.length];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">2</span>; i &lt; arr.length * <span class="number">2</span>; i *= <span class="number">2</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; (arr.length + i - <span class="number">1</span>) / i; j++) &#123;</span><br><span class="line">            <span class="keyword">int</span> left = i * j;</span><br><span class="line">            <span class="keyword">int</span> mid = left + i / <span class="number">2</span> &gt;= arr.length ? (arr.length - <span class="number">1</span>) : (left + i / <span class="number">2</span>);</span><br><span class="line">            <span class="keyword">int</span> right = i * (j + <span class="number">1</span>) - <span class="number">1</span> &gt;= arr.length ? (arr.length - <span class="number">1</span>) : (i * (j + <span class="number">1</span>) - <span class="number">1</span>);</span><br><span class="line">            <span class="keyword">int</span> start = left, l = left, m = mid;</span><br><span class="line">            <span class="keyword">while</span> (l &lt; mid &amp;&amp; m &lt;= right) &#123;</span><br><span class="line">                <span class="keyword">if</span> (arr[l] &lt; arr[m]) &#123;</span><br><span class="line">                    orderedArr[start++] = arr[l++];</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    orderedArr[start++] = arr[m++];</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">while</span> (l &lt; mid)</span><br><span class="line">                orderedArr[start++] = arr[l++];</span><br><span class="line">            <span class="keyword">while</span> (m &lt;= right)</span><br><span class="line">                orderedArr[start++] = arr[m++];</span><br><span class="line">            System.arraycopy(orderedArr, left, arr, left, right - left + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Python-4"><a href="#Python-4" class="headerlink" title="Python"></a>Python</h4><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">mergeSort</span>(<span class="params">nums</span>):</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(nums) &lt; <span class="number">2</span>:</span><br><span class="line">        <span class="keyword">return</span> nums</span><br><span class="line">    mid = <span class="built_in">len</span>(nums) // <span class="number">2</span></span><br><span class="line">    left = mergeSort(nums[:mid])</span><br><span class="line">    right = mergeSort(nums[mid:])</span><br><span class="line">    result = []</span><br><span class="line">    <span class="keyword">while</span> left <span class="keyword">and</span> right:</span><br><span class="line">        <span class="keyword">if</span> left[<span class="number">0</span>] &lt;= right[<span class="number">0</span>]:</span><br><span class="line">            result.append(left.pop(<span class="number">0</span>))</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            result.append(right.pop(<span class="number">0</span>))</span><br><span class="line">    <span class="keyword">if</span> left:</span><br><span class="line">        result += left</span><br><span class="line">    <span class="keyword">if</span> right:</span><br><span class="line">        result += right</span><br><span class="line">    <span class="keyword">return</span> result</span><br></pre></td></tr></table></figure></p><h2 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h2><p>快速排序（Quicksort），又称分区交换排序（partition-exchange sort），简称快排，一种排序算法，最早由东尼·霍尔提出。在平均状况下，排序 n 个项目要 O(nlogn) 次比较。在最坏状况下则需要 O(n²)次比较，但这种状况并不常见。事实上，快速排序(nlogn)通常明显比其他算法更快，因为它的内部循环（inner loop）可以在大部分的架构上很有效率地达成。</p><h3 id="算法步骤-5"><a href="#算法步骤-5" class="headerlink" title="算法步骤"></a>算法步骤</h3><ol><li>挑选基准值：从数列中挑出一个元素，称为“基准”（pivot）；</li><li>分割：重新排序数列，所有比基准值小的元素摆放在基准前面，所有比基准值大的元素摆在基准后面（与基准值相等的数可以到任何一边）。在这个分割结束之后，对基准值的排序就已经完成；</li><li>递归排序子序列：递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。</li></ol><h3 id="动图展示-5"><a href="#动图展示-5" class="headerlink" title="动图展示"></a>动图展示</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/sort_algorithm/quick_sort.gif" alt="快速排序"></p><h3 id="代码实现-5"><a href="#代码实现-5" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Java-5"><a href="#Java-5" class="headerlink" title="Java"></a>Java</h4><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">int</span>[] quickSort(<span class="keyword">int</span>[] arr, <span class="keyword">int</span> left, <span class="keyword">int</span> right) &#123;</span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> right = arr.length - <span class="number">1</span></span><br><span class="line">    <span class="comment">// 当传递的目标数组含有两个以上的元素时，进行递归调用。（即：当传递的目标数组只含有一个元素时，此趟排序结束）</span></span><br><span class="line">    <span class="keyword">if</span> (left &lt; right) &#123;</span><br><span class="line">        <span class="keyword">int</span> partitionIndex = partition(arr, left, right); <span class="comment">//获取关键值的下标（快排的核心）</span></span><br><span class="line">        quickSort(arr, left, partitionIndex - <span class="number">1</span>); <span class="comment">//递归调用，快排划分出来的左区间</span></span><br><span class="line">        quickSort(arr, partitionIndex + <span class="number">1</span>, right); <span class="comment">//递归调用，快排划分出来的右区间</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> arr;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">partition</span><span class="params">(<span class="keyword">int</span>[] arr, <span class="keyword">int</span> left, <span class="keyword">int</span> right)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 设定基准值（pivot）</span></span><br><span class="line">    <span class="keyword">int</span> pivot = left;</span><br><span class="line">    <span class="keyword">int</span> index = pivot + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = index; i &lt;= right; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (arr[i] &lt; arr[pivot]) &#123;</span><br><span class="line">            swap(arr, i, index);</span><br><span class="line">            index++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    swap(arr, pivot, index - <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">return</span> index - <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">swap</span><span class="params">(<span class="keyword">int</span>[] arr, <span class="keyword">int</span> i, <span class="keyword">int</span> j)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> temp = arr[i];</span><br><span class="line">    arr[i] = arr[j];</span><br><span class="line">    arr[j] = temp;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Python-5"><a href="#Python-5" class="headerlink" title="Python"></a>Python</h4><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">quickSort</span>(<span class="params">arr, left=<span class="literal">None</span>, right=<span class="literal">None</span></span>):</span></span><br><span class="line">    left = <span class="number">0</span> <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(left, (<span class="built_in">int</span>, <span class="built_in">float</span>)) <span class="keyword">else</span> left</span><br><span class="line">    right = <span class="built_in">len</span>(arr) - <span class="number">1</span> <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(right, (<span class="built_in">int</span>, <span class="built_in">float</span>)) <span class="keyword">else</span> right</span><br><span class="line">    <span class="keyword">if</span> left &lt; right:</span><br><span class="line">        partitionIndex = partition(arr, left, right)</span><br><span class="line">        quickSort(arr, left, partitionIndex - <span class="number">1</span>)</span><br><span class="line">        quickSort(arr, partitionIndex + <span class="number">1</span>, right)</span><br><span class="line">    <span class="keyword">return</span> arr</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">partition</span>(<span class="params">arr, left, right</span>):</span></span><br><span class="line">    pivot = left</span><br><span class="line">    index = pivot + <span class="number">1</span></span><br><span class="line">    i = index</span><br><span class="line">    <span class="keyword">while</span> i &lt;= right:</span><br><span class="line">        <span class="keyword">if</span> arr[i] &lt; arr[pivot]:</span><br><span class="line">            swap(arr, i, index)</span><br><span class="line">            index += <span class="number">1</span></span><br><span class="line">        i += <span class="number">1</span></span><br><span class="line">    swap(arr, pivot, index - <span class="number">1</span>)</span><br><span class="line">    <span class="keyword">return</span> index - <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">swap</span>(<span class="params">arr, i, j</span>):</span></span><br><span class="line">    arr[i], arr[j] = arr[j], arr[i]</span><br></pre></td></tr></table></figure></p><h2 id="堆排序"><a href="#堆排序" class="headerlink" title="堆排序"></a>堆排序</h2><p>堆排序（Heapsort）是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构，并同时满足堆的性质：即子节点的键值或索引总是小于（或者大于）它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法：</p><ul><li>大顶堆：每个节点的值都大于或等于其子节点的值，在堆排序算法中用于升序排列。</li><li>小顶堆：每个节点的值都小于或等于其子节点的值，在堆排序算法中用于降序排列。</li></ul><p>在堆的数据结构中，堆中的最大值总是位于根节点（在优先队列中使用堆的话堆中的最小值位于根节点）。堆中定义以下几种操作：</p><ul><li>最大堆调整（Max Heapify）：将堆的末端子节点作调整，使得子节点永远小于父节点。</li><li>创建最大堆（Build Max Heap）：将堆中的所有数据重新排序。</li><li>堆排序（HeapSort）：移除位在第一个数据的根节点，并做最大堆调整的递归运算。</li></ul><h3 id="算法步骤-6"><a href="#算法步骤-6" class="headerlink" title="算法步骤"></a>算法步骤</h3><ol><li>创建一个堆 H[0 ... n-1]；</li><li>把堆首（最大值）和堆尾互换；</li><li>把堆的尺寸缩小 1，并调用 shift_down(0)，目的是把新的数组顶端数据调整到相应位置；</li><li>重复步骤 2，直到堆的尺寸为 1。</li></ol><h3 id="动图展示-6"><a href="#动图展示-6" class="headerlink" title="动图展示"></a>动图展示</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/sort_algorithm/heap_sort.gif" alt="堆排序"></p><h3 id="代码实现-6"><a href="#代码实现-6" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Java-6"><a href="#Java-6" class="headerlink" title="Java"></a>Java</h4><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">  * 堆排序的主要入口方法，共两步。</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">heapSort</span><span class="params">(<span class="keyword">int</span>[] arr)</span> </span>&#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">      *  第一步：将数组堆化</span></span><br><span class="line"><span class="comment">      *  beginIndex = 第一个非叶子节点。</span></span><br><span class="line"><span class="comment">      *  从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。</span></span><br><span class="line"><span class="comment">      *  叶子节点可以看作已符合堆要求的节点，根节点就是它自己且自己以下值为最大。</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    <span class="keyword">int</span> len = arr.length - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">int</span> beginIndex = (arr.length &gt;&gt; <span class="number">1</span>)- <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = beginIndex; i &gt;= <span class="number">0</span>; i--)</span><br><span class="line">        maxHeapify(i, len);</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">      * 第二步：对堆化数据排序</span></span><br><span class="line"><span class="comment">      * 每次都是移出最顶层的根节点 A[0]，与最尾部节点位置调换，同时遍历长度 - 1。</span></span><br><span class="line"><span class="comment">      * 然后从新整理被换到根节点的末尾元素，使其符合堆的特性。</span></span><br><span class="line"><span class="comment">      * 直至未排序的堆长度为 0。</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = len; i &gt; <span class="number">0</span>; i--) &#123;</span><br><span class="line">        swap(<span class="number">0</span>, i);</span><br><span class="line">        maxHeapify(<span class="number">0</span>, i - <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">swap</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> j)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> temp = arr[i];</span><br><span class="line">    arr[i] = arr[j];</span><br><span class="line">    arr[j] = temp;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">  * 调整索引为 index 处的数据，使其符合堆的特性。</span></span><br><span class="line"><span class="comment">  *</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@param</span> index 需要堆化处理的数据的索引</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@param</span> len 未排序的堆（数组）的长度</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">maxHeapify</span><span class="params">(<span class="keyword">int</span> index, <span class="keyword">int</span> len)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> li = (index &lt;&lt; <span class="number">1</span>) + <span class="number">1</span>; <span class="comment">// 左子节点索引</span></span><br><span class="line">    <span class="keyword">int</span> ri = li + <span class="number">1</span>;           <span class="comment">// 右子节点索引</span></span><br><span class="line">    <span class="keyword">int</span> cMax = li;             <span class="comment">// 子节点值最大索引，默认左子节点。</span></span><br><span class="line">    <span class="keyword">if</span> (li &gt; len) <span class="keyword">return</span>;      <span class="comment">// 左子节点索引超出计算范围，直接返回。</span></span><br><span class="line">    <span class="keyword">if</span> (ri &lt;= len &amp;&amp; arr[ri] &gt; arr[li]) <span class="comment">// 先判断左右子节点，哪个较大。</span></span><br><span class="line">        cMax = ri;</span><br><span class="line">    <span class="keyword">if</span> (arr[cMax] &gt; arr[index]) &#123;</span><br><span class="line">        swap(cMax, index);      <span class="comment">// 如果父节点被子节点调换，</span></span><br><span class="line">        maxHeapify(cMax, len);  <span class="comment">// 则需要继续判断换下后的父节点是否符合堆的特性。</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Python-6"><a href="#Python-6" class="headerlink" title="Python"></a>Python</h4><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">heap_sort</span>(<span class="params">arr</span>):</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">sift_down</span>(<span class="params">start, end</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;最大堆调整&quot;&quot;&quot;</span></span><br><span class="line">        root = start</span><br><span class="line">        <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">            child = <span class="number">2</span> * root + <span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> child &gt; end:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            <span class="keyword">if</span> child + <span class="number">1</span> &lt;= end <span class="keyword">and</span> arr[child] &lt; arr[child + <span class="number">1</span>]:</span><br><span class="line">                child += <span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> arr[root] &lt; arr[child]:</span><br><span class="line">                arr[root], arr[child] = arr[child], arr[root]</span><br><span class="line">                root = child</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 创建最大堆</span></span><br><span class="line">    <span class="keyword">for</span> start <span class="keyword">in</span> xrange((<span class="built_in">len</span>(arr) - <span class="number">2</span>) // <span class="number">2</span>, -<span class="number">1</span>, -<span class="number">1</span>):</span><br><span class="line">        sift_down(start, <span class="built_in">len</span>(arr) - <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 堆排序</span></span><br><span class="line">    <span class="keyword">for</span> end <span class="keyword">in</span> xrange(<span class="built_in">len</span>(arr) - <span class="number">1</span>, <span class="number">0</span>, -<span class="number">1</span>):</span><br><span class="line">        arr[<span class="number">0</span>], arr[end] = arr[end], arr[<span class="number">0</span>]</span><br><span class="line">        sift_down(<span class="number">0</span>, end - <span class="number">1</span>)</span><br><span class="line">    <span class="keyword">return</span> arr</span><br></pre></td></tr></table></figure></p><h2 id="计数排序"><a href="#计数排序" class="headerlink" title="计数排序"></a>计数排序</h2><p>计数排序（Counting sort）是一种稳定的线性时间排序算法。计数排序使用一个额外的数组 C，其中第 i 个元素是待排序数组 A 中值等于 i 的元素的个数。然后根据数组 C 来将 A 中的元素排到正确的位置。</p><h3 id="算法步骤-7"><a href="#算法步骤-7" class="headerlink" title="算法步骤"></a>算法步骤</h3><p>通俗地理解，例如有 10 个年龄不同的人，统计出有 8 个人的年龄比 A 小，那 A 的年龄就排在第 9 位，用这个方法可以得到其他每个人的位置，也就排好了序。当然，年龄有重复时需要特殊处理（保证稳定性），这就是为什么最后要反向填充目标数组，以及将每个数字的统计减去 1。</p><ol><li>找出待排序的数组中最大和最小的元素</li><li>统计数组中每个值为 i 的元素出现的次数，存入数组 C 的第 i 项</li><li>对所有的计数累加（从 C 中的第一个元素开始，每一项和前一项相加）</li><li>反向填充目标数组：将每个元素 i 放在新数组的第 C[i] 项，每放一个元素就将 C[i] 减去 1</li></ol><h3 id="动图展示-7"><a href="#动图展示-7" class="headerlink" title="动图展示"></a>动图展示</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/sort_algorithm/counting_sort.gif" alt="计数排序"></p><h3 id="代码实现-7"><a href="#代码实现-7" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Java-7"><a href="#Java-7" class="headerlink" title="Java"></a>Java</h4><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">int</span>[] countingSort(<span class="keyword">int</span>[] arr) &#123;</span><br><span class="line">    <span class="keyword">int</span> maxValue = getMaxValue(arr)</span><br><span class="line">    <span class="keyword">int</span> bucketLen = maxValue + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">int</span>[] bucket = <span class="keyword">new</span> <span class="keyword">int</span>[bucketLen];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> value : arr) &#123;</span><br><span class="line">        bucket[value]++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> sortedIndex = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; bucketLen; j++) &#123;</span><br><span class="line">        <span class="keyword">while</span> (bucket[j] &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            arr[sortedIndex++] = j;</span><br><span class="line">            bucket[j]--;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> arr;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getMaxValue</span><span class="params">(<span class="keyword">int</span>[] arr)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> maxValue = arr[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> value : arr) &#123;</span><br><span class="line">        <span class="keyword">if</span> (maxValue &lt; value) &#123;</span><br><span class="line">            maxValue = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> maxValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Python-7"><a href="#Python-7" class="headerlink" title="Python"></a>Python</h4><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">countingSort</span>(<span class="params">arr</span>):</span></span><br><span class="line">    maxValue = arr[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">for</span> value <span class="keyword">in</span> arr:</span><br><span class="line">        <span class="keyword">if</span> maxValue &lt; value:</span><br><span class="line">            maxValue = value</span><br><span class="line"></span><br><span class="line">    bucketLen = maxValue + <span class="number">1</span></span><br><span class="line">    bucket = [<span class="number">0</span>] * bucketLen</span><br><span class="line">    sortedIndex = <span class="number">0</span></span><br><span class="line">    arrLen = <span class="built_in">len</span>(arr)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(arrLen):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> bucket[arr[i]]:</span><br><span class="line">            bucket[arr[i]] = <span class="number">0</span></span><br><span class="line">        bucket[arr[i]] += <span class="number">1</span></span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(bucketLen):</span><br><span class="line">        <span class="keyword">while</span> bucket[j] &gt; <span class="number">0</span>:</span><br><span class="line">            arr[sortedIndex] = j</span><br><span class="line">            sortedIndex += <span class="number">1</span></span><br><span class="line">            bucket[j] -= <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> arr</span><br></pre></td></tr></table></figure></p><h2 id="桶排序"><a href="#桶排序" class="headerlink" title="桶排序"></a>桶排序</h2><p>桶排序（Bucket sort）或所谓的箱排序，是一个排序算法，工作的原理是将数组分到有限数量的桶里。每个桶再个别排序（有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序）。</p><p>桶排序是计数排序的升级版。它利用了函数的映射关系，高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效，我们需要做到这两点：</p><p>在额外空间充足的情况下，尽量增大桶的数量使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中</p><h3 id="算法步骤-8"><a href="#算法步骤-8" class="headerlink" title="算法步骤"></a>算法步骤</h3><ol><li>设置一个定量的数组当作空桶子。</li><li>寻访序列，并且把项目一个一个放到对应的桶子去。</li><li>对每个不是空的桶子进行排序。</li><li>从不是空的桶子里把项目再放回原来的序列中。</li></ol><h3 id="动图展示-8"><a href="#动图展示-8" class="headerlink" title="动图展示"></a>动图展示</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/sort_algorithm/bucket_sort.gif" alt="桶排序"></p><h3 id="代码实现-8"><a href="#代码实现-8" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Java-8"><a href="#Java-8" class="headerlink" title="Java"></a>Java</h4><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">indexFor</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> min, <span class="keyword">int</span> step)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (a - min) / step;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">bucketSort</span><span class="params">(<span class="keyword">int</span>[] arr)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> max = arr[<span class="number">0</span>], min = arr[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> a : arr) &#123;</span><br><span class="line">        <span class="keyword">if</span> (max &lt; a)</span><br><span class="line">            max = a;</span><br><span class="line">        <span class="keyword">if</span> (min &gt; a)</span><br><span class="line">            min = a;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 该值也可根据实际情况选择</span></span><br><span class="line">    <span class="keyword">int</span> bucketNum = max / <span class="number">10</span> - min / <span class="number">10</span> + <span class="number">1</span>;</span><br><span class="line">    List buckList = <span class="keyword">new</span> ArrayList&lt;List&lt;Integer&gt;&gt;();</span><br><span class="line">    <span class="comment">// 创建桶</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= bucketNum; i++) &#123;</span><br><span class="line">        buckList.add(<span class="keyword">new</span> ArrayList&lt;Integer&gt;());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 将数据分配到各个桶中</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">        <span class="keyword">int</span> index = indexFor(arr[i], min, <span class="number">10</span>);</span><br><span class="line">        ((ArrayList&lt;Integer&gt;) buckList.get(index)).add(arr[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    ArrayList&lt;Integer&gt; bucket = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">int</span> index = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; bucketNum; i++) &#123;</span><br><span class="line">        bucket = (ArrayList&lt;Integer&gt;) buckList.get(i);</span><br><span class="line">        <span class="comment">// 对每个桶进行排序，这里使用了插入排序</span></span><br><span class="line">        insertSort(bucket);</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> k : bucket) &#123;</span><br><span class="line">            arr[index++] = k;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 把桶內元素使用插入排序</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">insertSort</span><span class="params">(List&lt;Integer&gt; bucket)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; bucket.size(); i++) &#123;</span><br><span class="line">        <span class="keyword">int</span> temp = bucket.get(i);</span><br><span class="line">        <span class="keyword">int</span> j = i - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span> (; j &gt;= <span class="number">0</span> &amp;&amp; bucket.get(j) &gt; temp; j--) &#123;</span><br><span class="line">            bucket.set(j + <span class="number">1</span>, bucket.get(j));</span><br><span class="line">        &#125;</span><br><span class="line">        bucket.set(j + <span class="number">1</span>, temp);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Python-8"><a href="#Python-8" class="headerlink" title="Python"></a>Python</h4><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">bucket_sort</span>(<span class="params">arr</span>):</span></span><br><span class="line">    max_num = <span class="built_in">max</span>(arr)</span><br><span class="line">    <span class="comment"># 不能使用[[]]*(max+1)，这样新建的空间中各个[]是共享内存的</span></span><br><span class="line">    buf = &#123;i: [] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">int</span>(max_num) + <span class="number">1</span>)&#125;</span><br><span class="line">    arr_len = <span class="built_in">len</span>(arr)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(arr_len):</span><br><span class="line">        num = arr[i]</span><br><span class="line">        <span class="comment"># 将相应范围内的数据加入到[]中</span></span><br><span class="line">        buf[<span class="built_in">int</span>(num)].append(num)</span><br><span class="line">    arr = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(buf)):</span><br><span class="line">        <span class="keyword">if</span> buf[i]:</span><br><span class="line">            <span class="comment"># 这里还需要对一个范围内的数据进行排序，然后再进行输出</span></span><br><span class="line">            arr.extend(<span class="built_in">sorted</span>(buf[i]))</span><br><span class="line">    <span class="keyword">return</span> arr</span><br></pre></td></tr></table></figure></p><h2 id="基数排序"><a href="#基数排序" class="headerlink" title="基数排序"></a>基数排序</h2><p>基数排序（Radix sort）是一种非比较型整数排序算法，其原理是将整数按位数切割成不同的数字，然后按每个位数分别比较。由于整数也可以表达字符串（比如名字或日期）和特定格式的浮点数，所以基数排序也不是只能使用于整数。</p><p>基数排序有两种方法：基数排序的方式可以采用 LSD（Least significant digital）或 MSD（Most significant digital），LSD 的排序方式由键值的最右边开始，而 MSD 则相反，由键值的最左边开始。</p><h3 id="算法步骤-9"><a href="#算法步骤-9" class="headerlink" title="算法步骤"></a>算法步骤</h3><ol><li>将所有待比较数值（正整数）统一为同样的数位长度，数位较短的数前面补零；</li><li>从最低位开始，依次进行一次排序；</li><li>这样从最低位排序一直到最高位排序完成以后，数列就变成一个有序序列。</li></ol><h3 id="动图展示-9"><a href="#动图展示-9" class="headerlink" title="动图展示"></a>动图展示</h3><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/sort_algorithm/radix_sort.gif" alt="基数排序"></p><h3 id="代码实现-9"><a href="#代码实现-9" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Java-9"><a href="#Java-9" class="headerlink" title="Java"></a>Java</h4><p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">radixSort</span><span class="params">(<span class="keyword">int</span>[] array)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> max = getMax(array);</span><br><span class="line">    <span class="keyword">int</span> bit = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span>(max / bit &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        radix(array, bit);</span><br><span class="line">        bit *= <span class="number">10</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">radix</span><span class="params">(<span class="keyword">int</span>[] array, <span class="keyword">int</span> bit)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span>[] temp = <span class="keyword">new</span> <span class="keyword">int</span>[array.length];</span><br><span class="line">    <span class="keyword">int</span>[] bucket = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">10</span>];</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; array.length; i++) &#123;</span><br><span class="line">        bucket[(array[i] / bit) % <span class="number">10</span>]++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; bucket.length; i++) &#123;</span><br><span class="line">        bucket[i] += bucket[i-<span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = array.length - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">        temp[bucket[(array[i] / bit) % <span class="number">10</span>] - <span class="number">1</span>] = array[i];</span><br><span class="line">        bucket[(array[i] / bit) % <span class="number">10</span>]--;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; temp.length; i++) &#123;</span><br><span class="line">        array[i] = temp[i];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getMax</span><span class="params">(<span class="keyword">int</span>[] array)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> max = array[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; array.length; i++)&#123;</span><br><span class="line">        <span class="keyword">if</span>(array[i] &gt; max) &#123;</span><br><span class="line">            max = array[i];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> max;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Python-9"><a href="#Python-9" class="headerlink" title="Python"></a>Python</h4><p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">radix_sort</span>(<span class="params">arr</span>):</span></span><br><span class="line">    n = <span class="built_in">len</span>(<span class="built_in">str</span>(<span class="built_in">max</span>(arr)))  <span class="comment"># 记录最大值的位数</span></span><br><span class="line">    <span class="keyword">for</span> k <span class="keyword">in</span> <span class="built_in">range</span>(n):  <span class="comment"># n 轮排序</span></span><br><span class="line">        <span class="comment"># 每一轮生成 10 个列表</span></span><br><span class="line">        bucket_list = [[] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]  <span class="comment"># 因为每一位数字都是 0~9，故建立 10 个桶</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> arr:</span><br><span class="line">            <span class="comment"># 按第 k 位放入到桶中</span></span><br><span class="line">            bucket_list[i // (<span class="number">10</span> ** k) % <span class="number">10</span>].append(i)</span><br><span class="line">        <span class="comment"># 按当前桶的顺序重排列表</span></span><br><span class="line">        arr = [j <span class="keyword">for</span> i <span class="keyword">in</span> bucket_list <span class="keyword">for</span> j <span class="keyword">in</span> i]</span><br><span class="line">    <span class="keyword">return</span> arr</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E7%AE%97%E6%B3%95/">算法</category>
      
      
      <comments>https://blog.eurkon.com/post/735e5788.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>HTML 特殊符号编码对照表</title>
      <link>https://blog.eurkon.com/post/65795b97.html</link>
      <guid>https://blog.eurkon.com/post/65795b97.html</guid>
      <pubDate>Fri, 12 Mar 2021 07:54:12 GMT</pubDate>
      
        
        
      <description>&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;符号&lt;/th&gt;
&lt;th&gt;命名实体&lt;/th&gt;
&lt;th&gt;十进制编码&lt;/th&gt;
&lt;th&gt;符号&lt;/th&gt;
&lt;th&gt;命名实体&lt;/th&gt;
&lt;th&gt;十进制编码&lt;/th&gt;
&lt;th&gt;符号&lt;</description>
        
      
      
      
      <content:encoded><![CDATA[<div class="table-container"><table><thead><tr><th>符号</th><th>命名实体</th><th>十进制编码</th><th>符号</th><th>命名实体</th><th>十进制编码</th><th>符号</th><th>命名实体</th><th>十进制编码</th></tr></thead><tbody><tr><td><code>Α</code></td><td><code>&amp;Alpha;</code></td><td><code>&amp;#913;</code></td><td><code>Β</code></td><td><code>&amp;Beta;</code></td><td><code>&amp;#914;</code></td><td><code>Γ</code></td><td><code>&amp;Gamma;</code></td><td><code>&amp;#915;</code></td></tr><tr><td><code>Δ</code></td><td><code>&amp;Delta;</code></td><td><code>&amp;#916;</code></td><td><code>Ε</code></td><td><code>&amp;Epsilon;</code></td><td><code>&amp;#917;</code></td><td><code>Ζ</code></td><td><code>&amp;Zeta;</code></td><td><code>&amp;#918;</code></td></tr><tr><td><code>Η</code></td><td><code>&amp;Eta;</code></td><td><code>&amp;#919;</code></td><td><code>Θ</code></td><td><code>&amp;Theta;</code></td><td><code>&amp;#920;</code></td><td><code>Ι</code></td><td><code>&amp;Iota;</code></td><td><code>&amp;#921;</code></td></tr><tr><td><code>Κ</code></td><td><code>&amp;Kappa;</code></td><td><code>&amp;#922;</code></td><td><code>Λ</code></td><td><code>&amp;Lambda;</code></td><td><code>&amp;#923;</code></td><td><code>Μ</code></td><td><code>&amp;Mu;</code></td><td><code>&amp;#924;</code></td></tr><tr><td><code>Ν</code></td><td><code>&amp;Nu;</code></td><td><code>&amp;#925;</code></td><td><code>Ξ</code></td><td><code>&amp;Xi;</code></td><td><code>&amp;#926;</code></td><td><code>Ο</code></td><td><code>&amp;Omicron;</code></td><td><code>&amp;#927;</code></td></tr><tr><td><code>Π</code></td><td><code>&amp;Pi;</code></td><td><code>&amp;#928;</code></td><td><code>Ρ</code></td><td><code>&amp;Rho;</code></td><td><code>&amp;#929;</code></td><td><code>Σ</code></td><td><code>&amp;Sigma;</code></td><td><code>&amp;#931;</code></td></tr><tr><td><code>Τ</code></td><td><code>&amp;Tau;</code></td><td><code>&amp;#932;</code></td><td><code>Υ</code></td><td><code>&amp;Upsilon;</code></td><td><code>&amp;#933;</code></td><td><code>Φ</code></td><td><code>&amp;Phi;</code></td><td><code>&amp;#934;</code></td></tr><tr><td><code>Χ</code></td><td><code>&amp;Chi;</code></td><td><code>&amp;#935;</code></td><td><code>Ψ</code></td><td><code>&amp;Psi;</code></td><td><code>&amp;#936;</code></td><td><code>Ω</code></td><td><code>&amp;Omega;</code></td><td><code>&amp;#937;</code></td></tr><tr><td><code>α</code></td><td><code>&amp;alpha;</code></td><td><code>&amp;#945;</code></td><td><code>β</code></td><td><code>&amp;beta;</code></td><td><code>&amp;#946;</code></td><td><code>γ</code></td><td><code>&amp;gamma;</code></td><td><code>&amp;#947;</code></td></tr><tr><td><code>δ</code></td><td><code>&amp;delta;</code></td><td><code>&amp;#948;</code></td><td><code>ε</code></td><td><code>&amp;epsilon;</code></td><td><code>&amp;#949;</code></td><td><code>ζ</code></td><td><code>&amp;zeta;</code></td><td><code>&amp;#950;</code></td></tr><tr><td><code>η</code></td><td><code>&amp;eta;</code></td><td><code>&amp;#951;</code></td><td><code>θ</code></td><td><code>&amp;theta;</code></td><td><code>&amp;#952;</code></td><td><code>ι</code></td><td><code>&amp;iota;</code></td><td><code>&amp;#953;</code></td></tr><tr><td><code>κ</code></td><td><code>&amp;kappa;</code></td><td><code>&amp;#954;</code></td><td><code>λ</code></td><td><code>&amp;lambda;</code></td><td><code>&amp;#955;</code></td><td><code>μ</code></td><td><code>&amp;mu;</code></td><td><code>&amp;#956;</code></td></tr><tr><td><code>ν</code></td><td><code>&amp;nu;</code></td><td><code>&amp;#957;</code></td><td><code>ξ</code></td><td><code>&amp;xi;</code></td><td><code>&amp;#958;</code></td><td><code>ο</code></td><td><code>&amp;omicron;</code></td><td><code>&amp;#959;</code></td></tr><tr><td><code>π</code></td><td><code>&amp;pi;</code></td><td><code>&amp;#960;</code></td><td><code>ρ</code></td><td><code>&amp;rho;</code></td><td><code>&amp;#961;</code></td><td><code>ς</code></td><td><code>&amp;sigmaf;</code></td><td><code>&amp;#962;</code></td></tr><tr><td><code>σ</code></td><td><code>&amp;sigma;</code></td><td><code>&amp;#963;</code></td><td><code>τ</code></td><td><code>&amp;tau;</code></td><td><code>&amp;#964;</code></td><td><code>υ</code></td><td><code>&amp;upsilon;</code></td><td><code>&amp;#965;</code></td></tr><tr><td><code>φ</code></td><td><code>&amp;phi;</code></td><td><code>&amp;#966;</code></td><td><code>χ</code></td><td><code>&amp;chi;</code></td><td><code>&amp;#967;</code></td><td><code>ψ</code></td><td><code>&amp;psi;</code></td><td><code>&amp;#968;</code></td></tr><tr><td><code>ω</code></td><td><code>&amp;omega;</code></td><td><code>&amp;#969;</code></td><td><code>ϑ</code></td><td><code>&amp;thetasym;</code></td><td><code>&amp;#977;</code></td><td><code>ϒ</code></td><td><code>&amp;upsih;</code></td><td><code>&amp;#978;</code></td></tr><tr><td><code>ϖ</code></td><td><code>&amp;piv;</code></td><td><code>&amp;#982;</code></td><td><code>•</code></td><td><code>&amp;bull;</code></td><td><code>&amp;#8226;</code></td><td><code>…</code></td><td><code>&amp;hellip;</code></td><td><code>&amp;#8230;</code></td></tr><tr><td><code>′</code></td><td><code>&amp;prime;</code></td><td><code>&amp;#8242;</code></td><td><code>″</code></td><td><code>&amp;Prime;</code></td><td><code>&amp;#8243;</code></td><td><code>‾</code></td><td><code>&amp;oline;</code></td><td><code>&amp;#8254;</code></td></tr><tr><td><code>⁄</code></td><td><code>&amp;frasl;</code></td><td><code>&amp;#8260;</code></td><td><code>℘</code></td><td><code>&amp;weierp;</code></td><td><code>&amp;#8472;</code></td><td><code>ℑ</code></td><td><code>&amp;image;</code></td><td><code>&amp;#8465;</code></td></tr><tr><td><code>ℜ</code></td><td><code>&amp;real;</code></td><td><code>&amp;#8476;</code></td><td><code>™</code></td><td><code>&amp;trade;</code></td><td><code>&amp;#8482;</code></td><td><code>ℵ</code></td><td><code>&amp;alefsym;</code></td><td><code>&amp;#8501;</code></td></tr><tr><td><code>←</code></td><td><code>&amp;larr;</code></td><td><code>&amp;#8592;</code></td><td><code>↑</code></td><td><code>&amp;uarr;</code></td><td><code>&amp;#8593;</code></td><td><code>→</code></td><td><code>&amp;rarr;</code></td><td><code>&amp;#8594;</code></td></tr><tr><td><code>↓</code></td><td><code>&amp;darr;</code></td><td><code>&amp;#8595;</code></td><td><code>↔</code></td><td><code>&amp;harr;</code></td><td><code>&amp;#8596;</code></td><td><code>↵</code></td><td><code>&amp;crarr;</code></td><td><code>&amp;#8629;</code></td></tr><tr><td><code>⇐</code></td><td><code>&amp;lArr;</code></td><td><code>&amp;#8656;</code></td><td><code>⇑</code></td><td><code>&amp;uArr;</code></td><td><code>&amp;#8657;</code></td><td><code>⇒</code></td><td><code>&amp;rArr;</code></td><td><code>&amp;#8658;</code></td></tr><tr><td><code>⇓</code></td><td><code>&amp;dArr;</code></td><td><code>&amp;#8659;</code></td><td><code>⇔</code></td><td><code>&amp;hArr;</code></td><td><code>&amp;#8660;</code></td><td><code>∀</code></td><td><code>&amp;forall;</code></td><td><code>&amp;#8704;</code></td></tr><tr><td><code>∂</code></td><td><code>&amp;part;</code></td><td><code>&amp;#8706;</code></td><td><code>∃</code></td><td><code>&amp;exist;</code></td><td><code>&amp;#8707;</code></td><td><code>∅</code></td><td><code>&amp;empty;</code></td><td><code>&amp;#8709;</code></td></tr><tr><td><code>∇</code></td><td><code>&amp;nabla;</code></td><td><code>&amp;#8711;</code></td><td><code>∈</code></td><td><code>&amp;isin;</code></td><td><code>&amp;#8712;</code></td><td><code>∉</code></td><td><code>&amp;notin;</code></td><td><code>&amp;#8713;</code></td></tr><tr><td><code>∋</code></td><td><code>&amp;ni;</code></td><td><code>&amp;#8715;</code></td><td><code>∏</code></td><td><code>&amp;prod;</code></td><td><code>&amp;#8719;</code></td><td><code>∑</code></td><td><code>&amp;sum;</code></td><td><code>&amp;#8722;</code></td></tr><tr><td><code>−</code></td><td><code>&amp;minus;</code></td><td><code>&amp;#8722;</code></td><td><code>∗</code></td><td><code>&amp;lowast;</code></td><td><code>&amp;#8727;</code></td><td><code>√</code></td><td><code>&amp;radic;</code></td><td><code>&amp;#8730;</code></td></tr><tr><td><code>∝</code></td><td><code>&amp;prop;</code></td><td><code>&amp;#8733;</code></td><td><code>∞</code></td><td><code>&amp;infin;</code></td><td><code>&amp;#8734;</code></td><td><code>∠</code></td><td><code>&amp;ang;</code></td><td><code>&amp;#8736;</code></td></tr><tr><td><code>∧</code></td><td><code>&amp;and;</code></td><td><code>&amp;#8869;</code></td><td><code>∨</code></td><td><code>&amp;or;</code></td><td><code>&amp;#8870;</code></td><td><code>∩</code></td><td><code>&amp;cap;</code></td><td><code>&amp;#8745;</code></td></tr><tr><td><code>∪</code></td><td><code>&amp;cup;</code></td><td><code>&amp;#8746;</code></td><td><code>∫</code></td><td><code>&amp;int;</code></td><td><code>&amp;#8747;</code></td><td><code>∴</code></td><td><code>&amp;there4;</code></td><td><code>&amp;#8756;</code></td></tr><tr><td><code>∼</code></td><td><code>&amp;sim;</code></td><td><code>&amp;#8764;</code></td><td><code>≅</code></td><td><code>&amp;cong;</code></td><td><code>&amp;#8773;</code></td><td><code>≈</code></td><td><code>&amp;asymp;</code></td><td><code>&amp;#8773;</code></td></tr><tr><td><code>≠</code></td><td><code>&amp;ne;</code></td><td><code>&amp;#8800;</code></td><td><code>≡</code></td><td><code>&amp;equiv;</code></td><td><code>&amp;#8801;</code></td><td><code>≤</code></td><td><code>&amp;le;</code></td><td><code>&amp;#8804;</code></td></tr><tr><td><code>≥</code></td><td><code>&amp;ge;</code></td><td><code>&amp;#8805;</code></td><td><code>⊂</code></td><td><code>&amp;sub;</code></td><td><code>&amp;#8834;</code></td><td><code>⊃</code></td><td><code>&amp;sup;</code></td><td><code>&amp;#8835;</code></td></tr><tr><td><code>⊄</code></td><td><code>&amp;nsub;</code></td><td><code>&amp;#8836;</code></td><td><code>⊆</code></td><td><code>&amp;sube;</code></td><td><code>&amp;#8838;</code></td><td><code>⊇</code></td><td><code>&amp;supe;</code></td><td><code>&amp;#8839;</code></td></tr><tr><td><code>⊕</code></td><td><code>&amp;oplus;</code></td><td><code>&amp;#8853;</code></td><td><code>⊗</code></td><td><code>&amp;otimes;</code></td><td><code>&amp;#8855;</code></td><td><code>⊥</code></td><td><code>&amp;perp;</code></td><td><code>&amp;#8869;</code></td></tr><tr><td><code>⋅</code></td><td><code>&amp;sdot;</code></td><td><code>&amp;#8901;</code></td><td><code>⌈</code></td><td><code>&amp;lceil;</code></td><td><code>&amp;#8968;</code></td><td><code>⌉</code></td><td><code>&amp;rceil;</code></td><td><code>&amp;#8969;</code></td></tr><tr><td><code>⌊</code></td><td><code>&amp;lfloor;</code></td><td><code>&amp;#8970;</code></td><td><code>⌋</code></td><td><code>&amp;rfloor;</code></td><td><code>&amp;#8971;</code></td><td><code>◊</code></td><td><code>&amp;loz;</code></td><td><code>&amp;#9674;</code></td></tr><tr><td><code>♠</code></td><td><code>&amp;spades;</code></td><td><code>&amp;#9824;</code></td><td><code>♣</code></td><td><code>&amp;clubs;</code></td><td><code>&amp;#9827;</code></td><td><code>♥</code></td><td><code>&amp;hearts;</code></td><td><code>&amp;#9829;</code></td></tr><tr><td><code>♦</code></td><td><code>&amp;diams;</code></td><td><code>&amp;#9830;</code></td><td><code> </code></td><td><code>&amp;nbsp;</code></td><td><code>&amp;#160;</code></td><td><code>¡</code></td><td><code>&amp;iexcl;</code></td><td><code>&amp;#161;</code></td></tr><tr><td><code>¢</code></td><td><code>&amp;cent;</code></td><td><code>&amp;#162;</code></td><td><code>£</code></td><td><code>&amp;pound;</code></td><td><code>&amp;#163;</code></td><td><code>¤</code></td><td><code>&amp;curren;</code></td><td><code>&amp;#164;</code></td></tr><tr><td><code>¥</code></td><td><code>&amp;yen;</code></td><td><code>&amp;#165;</code></td><td><code>¦</code></td><td><code>&amp;brvbar;</code></td><td><code>&amp;#166;</code></td><td><code>§</code></td><td><code>&amp;sect;</code></td><td><code>&amp;#167;</code></td></tr><tr><td><code>¨</code></td><td><code>&amp;uml;</code></td><td><code>&amp;#168;</code></td><td><code>©</code></td><td><code>&amp;copy;</code></td><td><code>&amp;#169;</code></td><td><code>ª</code></td><td><code>&amp;ordf;</code></td><td><code>&amp;#170;</code></td></tr><tr><td><code>«</code></td><td><code>&amp;laquo;</code></td><td><code>&amp;#171;</code></td><td><code>¬</code></td><td><code>&amp;not;</code></td><td><code>&amp;#172;</code></td><td><code>­</code></td><td><code>&amp;shy;</code></td><td><code>&amp;#173;</code></td></tr><tr><td><code>®</code></td><td><code>&amp;reg;</code></td><td><code>&amp;#174;</code></td><td><code>¯</code></td><td><code>&amp;macr;</code></td><td><code>&amp;#175;</code></td><td><code>°</code></td><td><code>&amp;deg;</code></td><td><code>&amp;#176;</code></td></tr><tr><td><code>±</code></td><td><code>&amp;plusmn;</code></td><td><code>&amp;#177;</code></td><td><code>²</code></td><td><code>&amp;sup2;</code></td><td><code>&amp;#178;</code></td><td><code>³</code></td><td><code>&amp;sup3;</code></td><td><code>&amp;#179;</code></td></tr><tr><td><code>´</code></td><td><code>&amp;acute;</code></td><td><code>&amp;#180;</code></td><td><code>µ</code></td><td><code>&amp;micro;</code></td><td><code>&amp;#181;</code></td><td><code>&quot;</code></td><td><code>&amp;quot;</code></td><td><code>&amp;#34;</code></td></tr><tr><td><code>&lt;</code></td><td><code>&amp;lt;</code></td><td><code>&amp;#60;</code></td><td><code>&gt;</code></td><td><code>&amp;gt;</code></td><td><code>&amp;#62;</code></td><td><code>&#39;</code></td><td><code>&amp;apos;</code></td><td><code>&amp;#39;</code></td></tr></tbody></table></div>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%88%86%E4%BA%AB%E8%BD%AC%E8%BD%BD/">分享转载</category>
      
      
      <category domain="https://blog.eurkon.com/tags/HTML/">HTML</category>
      
      <category domain="https://blog.eurkon.com/tags/%E6%96%87%E6%A1%A3/">文档</category>
      
      
      <comments>https://blog.eurkon.com/post/65795b97.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>特殊符号</title>
      <link>https://blog.eurkon.com/post/73bb9016.html</link>
      <guid>https://blog.eurkon.com/post/73bb9016.html</guid>
      <pubDate>Wed, 10 Mar 2021 02:23:12 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;编号序号&quot;&gt;&lt;a href=&quot;#编号序号&quot; class=&quot;headerlink&quot; title=&quot;编号序号&quot;&gt;&lt;/a&gt;编号序号&lt;/h2&gt;&lt;p&gt;①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⓪&lt;/p&gt;
&lt;p&gt;❶❷❸❹❺❻❼❽❾❿⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴&lt;/p&gt;
&lt;p&gt;㊀㊁㊂㊃㊄</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="编号序号"><a href="#编号序号" class="headerlink" title="编号序号"></a>编号序号</h2><p>①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⓪</p><p>❶❷❸❹❺❻❼❽❾❿⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴</p><p>㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉</p><p>一二三四五六七八九十</p><p>⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇</p><p>⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛</p><p>ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ</p><p>ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ</p><p>ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ</p><p>⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵</p><p>⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉</p><h2 id="数学符号"><a href="#数学符号" class="headerlink" title="数学符号"></a>数学符号</h2><p>﹢﹣×÷±/≌∽≦≧≒﹤﹥≈≡≠=≤≥&lt;&gt;≮≯≂≃≄≅≆≇≈≉≊≋≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≢≣≨≩⊰⊱⋛⋚∷∶∝∞∧∨∑∏∪∩∈∵∴⊥∥∠⌒⊙√∟⊿㏒㏑⅟½⅓⅕⅙⅛⅔⅖⅚⅜¾⅗⅝⅞⅘∫∬∭∮∯∰∱∲∳%℅‰‱øØπ</p><h2 id="标点符号"><a href="#标点符号" class="headerlink" title="标点符号"></a>标点符号</h2><p>。，、&#39;：∶；?‘’“”〝〞ˆˇ﹕︰﹔﹖﹑·¨….¸;！´？！～—ˉ|‖&quot;〃`@﹫¡¿﹏﹋﹌︴々﹟#﹩$﹠&amp;﹪%*﹡﹢﹦﹤‐￣¯―﹨ˆ˜﹍﹎+=&lt;<em>_-\ˇ~﹉﹊（）〈〉‹›﹛﹜『』〖〗[]《》〔〕{}「」【】︵︷^︹︽</em>﹁﹃︻︶︸﹀︺︾ˉ﹂﹄︼❝❞</p><h2 id="上标符号"><a href="#上标符号" class="headerlink" title="上标符号"></a>上标符号</h2><p>ᵃ ᵇ ᶜ ᵈ ᵉ ᵍ ʰ ⁱ ʲ ᵏ ˡ ᵐ ⁿ ᵒ ᵖ ᵒ⃒ ʳ ˢ ᵗ ᵘ ᵛ ʷ ˣ ʸ ᙆ ᴬ ᴮ ᒼ ᴰ ᴱ ᴳ ᴴ ᴵ ᴶ ᴷ ᴸ ᴹ ᴺ ᴼ ᴾ ᴼ̴ ᴿ ˢ ᵀ ᵁ ᵂ ˣ ᵞ ᙆ ꝰ ˀ ˁ ˤ ꟸ ꭜ ʱ ꭝ ꭞ ʴ ʵ ʶ ꭟ ˠ ꟹ ᴭ ᴯ ᴲ ᴻ ᴽ ᵄ ᵅ ᵆ ᵊ ᵋ ᵌ ᵑ ᵓ ᵚ ᵝ ᵞ ᵟ ᵠ ᵡ ᵎ ᵔ ᵕ ᵙ ᵜ ᶛ ᶜ ᶝ ᶞ ᶟ ᶡ ᶣ ᶤ ᶥ ᶦ ᶧ ᶨ ᶩ ᶪ ᶫ ᶬ ᶭ ᶮ ᶯ ᶰ ᶱ ᶲ ᶳ ᶴ ᶵ ᶶ ᶷ ᶸ ᶹ ᶺ ᶼ ᶽ ᶾ ᶿ ꚜ ꚝ ჼ ᒃ ᕻ ᑦ ᒄ ᕪ ᑋ ᑊ ᔿ ᐢ ᣕ ᐤ ᣖ ᣴ ᣗ ᔆ ᙚ ᐡ ᘁ ᐜ ᕽ ᙆ ᙇ ᒼ ᣳ ᒢ ᒻ ᔿ ᐤ ᣖ ᣵ ᙚ ᐪ ᓑ ᘁ ᐜ ᕽ ᙆ ᙇ ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ˂ ˃ ⁽ ⁾ ˙ * º ㆒㆓㆔㆕㆖㆗㆘㆙㆚㆛㆜㆝㆞</p><h2 id="下标符号"><a href="#下标符号" class="headerlink" title="下标符号"></a>下标符号</h2><p>₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊ ₋ ₌ ₍ ₎ ₐ ₑ ₒ ₓ ₔ ₕ ₖ ₗ ₘ ₙ ₚ ₛ ₜ ᵤ ᵥ ₓ ᙮ ᵤ ᵩ ᵦ ₗ ˪ ៳ ៷ ₒ ᵨ ₛ ៴ ᵤ ᵪ ᵧ</p><h2 id="单位符号"><a href="#单位符号" class="headerlink" title="单位符号"></a>单位符号</h2><p>°′″%℃℉Å﹪‰㎡㏕㎜㎝㎞㏎m³㎎㎏㏄º○¤%º¹²³⁴⁵⁶⁷⁸⁹⁰⁺⁻⁼⁽⁾ʲʰʳʷʸⁿ</p><h2 id="箭头符号"><a href="#箭头符号" class="headerlink" title="箭头符号"></a>箭头符号</h2><p>↑↓←→↖↗↘↙↔↕➻➼➽➸➳➺➻➴➵➶➷➹▶►▷◁◀◄«»➩➪➫➬➭➮➯➱⏎➲➾➔➘➙➚➛➜➝➞➟➠➡➢➣➤➥➦➧➨↚↛↜↝↞↟↠↠↡↢↣↤↤↥↦↧↨⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑⇒⇓⇔⇖⇗⇘⇙⇜↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹☇☈↼↽↾↿⇀⇁⇂⇃⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪↺↻⇚⇛</p><h2 id="符号图案"><a href="#符号图案" class="headerlink" title="符号图案"></a>符号图案</h2><p>✐✎✏✑✒✉✁✂✃✄✆✉☎☏☑√☐☒✗ㄨ✘✕✖✖☢☠☣✈★☆✡囍㍿☯☰☲☱☴☵☶☳☷☜☞☚☛☟♤♧♡♢♠♣♥♦웃유❖☪✿♂♀✪✯☭➳卍卐√×■□◆●○✙☺☻❀⚘♔♕♖♗♙♚♛♜♝♞♟♂♀♠♣❤☜☞⊙☼▧▨♨▪◊◦▣▤▥▦▩◘◈◇♬♪♩♭♪の→あぃ￡Ю〓§♤¤✲❈✿✲❈➹☀☂☁【】┱┲❣✚✪✣✤✥✦※❉❥❦❧❃❂❁✄☪☣☢☠☭ღ☀☁☂☃☄☇☈⊙☊☋☌☍ⓛⓞⓥⓔ╬『』∴☀♫♬♩♭♪﹌の►◄♨↘▀▄█▌◦☼♪の→▬♦◊◦♠♣▣۰··۰►◄♨▪▫☼♦⊙●○①⊕Θ¤㊣♀◆◇◣◢◥▲▼△▽⊿◤◥✡✓✔✕✖♂♀☜☞⊙◎►◄♨◐◑▪▫☼♦▐░▒▬♦◊◘◙◦☼▣▤▥▦▩◘◙◈♫♬♪♩♭♪✄☪☣☢☠♯♩♪♫♬♭♮☪ºº₪¤큐«»™♂✿｡◕‿◕｡☹⊰⊱❀҉এృةمʚΐɞʚɞﻬ๑ف๓ق͜✿҉͜ღ҉℘ೄζั͡ตԅ️✾ೄ೨Ͽ҉✿ೃ✿҉͜✿ۣζั͡✿يℳ❀͜҉　҉҉͡꧁༺๑๑༻꧂დ﹏⃣ぃ￡</p><h2 id="希腊字母"><a href="#希腊字母" class="headerlink" title="希腊字母"></a>希腊字母</h2><p>ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζνξοπρσηθικλμτυφχψω</p><h2 id="俄语字母"><a href="#俄语字母" class="headerlink" title="俄语字母"></a>俄语字母</h2><p>АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя</p><h2 id="汉语拼音"><a href="#汉语拼音" class="headerlink" title="汉语拼音"></a>汉语拼音</h2><p>āáǎàōóǒòēéěèīíǐìūúǔùǖǘǚǜüêɑńňɡㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩ</p><h2 id="中文字符"><a href="#中文字符" class="headerlink" title="中文字符"></a>中文字符</h2><p>卍卐卄巜朤氺曱甴囍兀々〆のぁ〡〢〣〤〥〦〧〨〩㊎㊍㊌㊋㊏㊚㊛㊐㊊㊣㊤㊥㊦㊧㊨㊒㊫㊑㊓㊔㊕㊖㊗㊘㊜㊝㊞㊟㊠㊡㊢㊩㊪㊬㊭㊮㊯㊰㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉</p><h2 id="韩国符号"><a href="#韩国符号" class="headerlink" title="韩国符号"></a>韩国符号</h2><p>ㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎㅏㅐㅑㅒㅓㅔㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅥㅦㅧㅨㅩㅪㅫㅬㅭㅮㅯㅰㅱㅲㅳㅴㅵㅶㅷㅸㅹㅺㅻㅼㅽㅾㅿㆀㆁㆂㆃㆄㆅㆆㆇㆈㆉㆊ</p><h2 id="天气符"><a href="#天气符" class="headerlink" title="天气符"></a>天气符</h2><p>ϟ☀☁☂☃☄☉☼☽☾♁♨❄❅❆</p><h2 id="制表符"><a href="#制表符" class="headerlink" title="制表符"></a>制表符</h2><p>─━│┃╌╍╎╏┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋╪╫╬═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╳╔╗╝╚╬═╓╩┠┨┯┷┏┓┗┛┳⊥﹃﹄┌╮╭╯╰</p><h2 id="国际象棋符号"><a href="#国际象棋符号" class="headerlink" title="国际象棋符号"></a>国际象棋符号</h2><p>♚　♛　♝　♞　♜　♟　♔　♕　♗　♘　♖　♟</p><h2 id="货币符号"><a href="#货币符号" class="headerlink" title="货币符号"></a>货币符号</h2><p>¥：人民币符号。</p><p>฿：泰铢标志（被使用在泰国），分标志（美元、欧元和其它货币细分）。</p><p>₡：col3on 标志（被使用在哥斯达黎加和在萨尔瓦多）。</p><p>₠：ECU 标志（不广泛被应用, 和历史; 由欧洲替换）。</p><p>₢：克鲁赛罗标志（以前被使用在巴西）。</p><p>€：欧元标志。</p><p>₩：韩元、朝鲜元标志。</p><p>$：美元标志（并且被使用为许多其它货币在美洲, 譬如不同的比索, 和以前为葡萄牙埃斯库多作为 cifr6ao）。</p><p>￡：英磅标志（大不列颠及北爱尔兰联合王国/英国 货币符号）。</p><p>₯：德拉克马标志（以前被使用在希腊）。</p><p>₫：oong 标志（被使用在越南）。</p><p>円：日元的货币符。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%88%86%E4%BA%AB%E8%BD%AC%E8%BD%BD/">分享转载</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E6%96%87%E6%A1%A3/">文档</category>
      
      
      <comments>https://blog.eurkon.com/post/73bb9016.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>HDFS Shell 命令</title>
      <link>https://blog.eurkon.com/post/d1e7dfd3.html</link>
      <guid>https://blog.eurkon.com/post/d1e7dfd3.html</guid>
      <pubDate>Fri, 05 Mar 2021 02:31:14 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;FS-Shell&quot;&gt;&lt;a href=&quot;#FS-Shell&quot; class=&quot;headerlink&quot; title=&quot;FS Shell&quot;&gt;&lt;/a&gt;FS Shell&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;调用文件系统（FS）的 Shell 命令应使用 &lt;code&gt;bin/had</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="FS-Shell"><a href="#FS-Shell" class="headerlink" title="FS Shell"></a>FS Shell</h2><ul><li><p>调用文件系统（FS）的 Shell 命令应使用 <code>bin/hadoop fs &lt;args&gt;</code> 的形式。</p></li><li><p>所有的 FS shell 命令使用 URI 路径作为参数。</p></li><li><p>URI 格式是 <code>scheme://authority/path</code>。对 HDFS 文件系统，<code>scheme</code> 是 <code>hdfs</code>，对本地文件系统，<code>scheme</code> 是 <code>file</code>。其中 <code>scheme</code> 和 <code>authority</code> 参数都是可选的，如果未加指定，就会使用配置中指定的默认 <code>scheme</code>。</p></li><li><p>一个 HDFS 文件或目录比如 <code>/parent/child</code> 可以表示成 <code>hdfs://namenode:namenodeport/parent/child</code>，或者更简单的 <code>/parent/child</code>（假设你配置文件中的默认值是 <code>namenode:namenodeport</code>）。</p></li><li><p>大多数 FS Shell 命令的行为和对应的 Unix Shell 命令类似，不同之处会在下面介绍各命令使用详情时指出。出错信息会输出到 <code>stderr</code>，其他信息输出到 <code>stdout</code>。</p></li></ul><h2 id="cat"><a href="#cat" class="headerlink" title="cat"></a>cat</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -cat URI [URI …]</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：将路径指定文件的内容输出到 <code>stdout</code>。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -cat hdfs://host1:port1/file1 hdfs://host2:port2/file2</span><br><span class="line">hadoop fs -cat file:///file3 /user/hadoop/file4</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="chgrp"><a href="#chgrp" class="headerlink" title="chgrp"></a>chgrp</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -chgrp [-R] GROUP URI [URI …]`</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：改变文件所属的组。使用 <code>-R</code> 将使改变在目录结构下递归进行。命令的使用者必须是文件的所有者或者超级用户。</p></li></ul><h2 id="chmod"><a href="#chmod" class="headerlink" title="chmod"></a>chmod</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -chmod [-R] &lt;MODE[,MODE]... | OCTALMODE&gt; URI [URI …]</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：改变文件的权限。使用 <code>-R</code> 将使改变在目录结构下递归进行。命令的使用者必须是文件的所有者或者超级用户。</p></li></ul><h2 id="chown"><a href="#chown" class="headerlink" title="chown"></a>chown</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -chown [-R] [OWNER][:[GROUP]] URI [URI ]</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：改变文件的拥有者。使用 <code>-R</code> 将使改变在目录结构下递归进行。命令的使用者必须是超级用户。</p></li></ul><h2 id="copyFromLocal"><a href="#copyFromLocal" class="headerlink" title="copyFromLocal"></a>copyFromLocal</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -copyFromLocal &lt;localsrc&gt; URI</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：除了限定源路径是一个本地文件外，和 <a href="#put">put</a> 命令相似。</p></li></ul><h2 id="copyToLocal"><a href="#copyToLocal" class="headerlink" title="copyToLocal"></a>copyToLocal</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -copyToLocal [-ignorecrc] [-crc] URI &lt;localdst&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：除了限定目标路径是一个本地文件外，和 <a href="#get">get</a> 命令类似。</p></li></ul><h2 id="cp"><a href="#cp" class="headerlink" title="cp"></a>cp</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -cp URI [URI …] &lt;dest&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：将文件从源路径复制到目标路径。这个命令允许有多个源路径，此时目标路径必须是一个目录。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -cp /user/hadoop/file1 /user/hadoop/file2</span><br><span class="line">hadoop fs -cp /user/hadoop/file1 /user/hadoop/file2 /user/hadoop/dir</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="du"><a href="#du" class="headerlink" title="du"></a>du</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -du URI [URI …]</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：显示目录中所有文件的大小，或者当只指定一个文件时，显示此文件的大小。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -du /user/hadoop/dir1 /user/hadoop/file1 hdfs://host:port/user/hadoop/dir1</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="dus"><a href="#dus" class="headerlink" title="dus"></a>dus</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -dus &lt;args&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：显示文件的大小。</p></li></ul><h2 id="expunge"><a href="#expunge" class="headerlink" title="expunge"></a>expunge</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -expunge</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：清空回收站。请参考 <a href="http://hadoop.apache.org/docs/r1.0.4/cn/hdfs_design.html">HDFS 设计文档</a> 以获取更多关于回收站特性的信息。</p></li></ul><h2 id="get"><a href="#get" class="headerlink" title="get"></a>get</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -get [-ignorecrc] [-crc] &lt;src&gt; &lt;localdst&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：复制文件到本地文件系统。可用 <code>-ignorecrc</code> 选项复制 CRC 校验失败的文件。使用 <code>-crc</code> 选项复制文件以及 CRC 信息。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -get /user/hadoop/file localfile</span><br><span class="line">hadoop fs -get hdfs://host:port/user/hadoop/file localfile</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="getmerge"><a href="#getmerge" class="headerlink" title="getmerge"></a>getmerge</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -getmerge &lt;src&gt; &lt;localdst&gt; [addnl]</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：接受一个源目录和一个目标文件作为输入，并且将源目录中所有的文件连接成本地目标文件。<code>addnl</code> 是可选的，用于指定在每个文件结尾添加一个换行符。</p></li></ul><h2 id="ls"><a href="#ls" class="headerlink" title="ls"></a>ls</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -ls &lt;args&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：</p><ul><li><p>如果是文件，则按照如下格式返回文件信息：</p><p>  文件名 | &lt;副本数&gt; | 文件大小 | 修改日期 | 修改时间 | 权限 | 用户 ID | 组 ID</p></li><li><p>如果是目录，则返回它直接子文件的一个列表，就像在 Unix 中一样。目录返回列表的信息：</p><p>  目录名 | <code>&lt;dir&gt;</code> | 修改日期 | 修改时间 | 权限 | 用户 ID | 组 ID</p></li></ul></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -ls /user/hadoop/file1 /user/hadoop/file2 hdfs://host:port/user/hadoop/dir1 /nonexistentfile</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="lsr"><a href="#lsr" class="headerlink" title="lsr"></a>lsr</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -lsr &lt;args&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：<a href="#ls">ls</a> 命令的递归版本。类似于 Unix 中的 <code>ls -R</code>。</p></li></ul><h2 id="mkdir"><a href="#mkdir" class="headerlink" title="mkdir"></a>mkdir</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -mkdir &lt;paths&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：接受路径指定的 <code>URI</code> 作为参数，创建这些目录。其行为类似于 Unix 的 <code>mkdir -p</code>，它会创建路径中的各级父目录。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -mkdir /user/hadoop/dir1 /user/hadoop/dir2</span><br><span class="line">hadoop fs -mkdir hdfs://host1:port1/user/hadoop/dir hdfs://host2:port2/user/hadoop/dir</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="movefromLocal"><a href="#movefromLocal" class="headerlink" title="movefromLocal"></a>movefromLocal</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dfs -moveFromLocal &lt;src&gt; &lt;dst&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：输出一个 <code>not implemented</code> 信息。</p></li></ul><h2 id="mv"><a href="#mv" class="headerlink" title="mv"></a>mv</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -mv URI [URI …] &lt;dest&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：将文件从源路径移动到目标路径。这个命令允许有多个源路径，此时目标路径必须是一个目录。不允许在不同的文件系统间移动文件。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -mv /user/hadoop/file1 /user/hadoop/file2</span><br><span class="line">hadoop fs -mv hdfs://host:port/file1 hdfs://host:port/file2 hdfs://host:port/file3 hdfs://host:port/dir1</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="put"><a href="#put" class="headerlink" title="put"></a>put</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -put &lt;localsrc&gt; ... &lt;dst&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：从本地文件系统中复制单个或多个源路径到目标文件系统。也支持从标准输入中读取输入写入目标文件系统。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -put localfile /user/hadoop/hadoopfile</span><br><span class="line">hadoop fs -put localfile1 localfile2 /user/hadoop/hadoopdir</span><br><span class="line">hadoop fs -put localfile hdfs://host:port/hadoop/hadoopfile</span><br><span class="line">hadoop fs -put - hdfs://host:port/hadoop/hadoopfile</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="rm"><a href="#rm" class="headerlink" title="rm"></a>rm</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -rm URI [URI …]</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：删除指定的文件。只删除非空目录和文件。请参考 <a href="#rmr">rmr</a> 命令了解递归删除。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -rm hdfs://host:port/file /user/hadoop/emptydir</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="rmr"><a href="#rmr" class="headerlink" title="rmr"></a>rmr</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -rmr URI [URI …]</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：<a href="#rm">rm</a> 命令的递归版本。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -rmr /user/hadoop/dir</span><br><span class="line">hadoop fs -rmr hdfs://host:port/user/hadoop/dir</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="setrep"><a href="#setrep" class="headerlink" title="setrep"></a>setrep</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -setrep [-R] &lt;path&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：改变一个文件的副本系数。<code>-R</code> 选项用于递归改变目录下所有文件的副本系数。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -setrep -w 3 -R /user/hadoop/dir1</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="stat"><a href="#stat" class="headerlink" title="stat"></a>stat</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -stat URI [URI …]</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：返回指定路径的统计信息。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -stat path</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="tail"><a href="#tail" class="headerlink" title="tail"></a>tail</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -tail [-f] URI</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：将文件尾部 1K 字节的内容输出到 <code>stdout</code>。支持 <code>-f</code> 选项，行为和 Unix 中一致。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -tail pathname</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul><h2 id="test"><a href="#test" class="headerlink" title="test"></a>test</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -test -[ezd] URI</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：</p><ul><li><code>e</code> 检查文件是否存在。如果存在则返回 <code>0</code>。</li><li><code>z</code> 检查文件是否是 0 字节。如果是则返回 <code>0</code>。</li><li><code>d</code> 如果路径是个目录，则返回 <code>1</code>，否则返回 <code>0</code>。</li></ul></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -test -e filename</span><br></pre></td></tr></table></figure></p></li></ul><h2 id="text"><a href="#text" class="headerlink" title="text"></a>text</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -text &lt;src&gt;</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：将源文件输出为文本格式。允许的格式是 <code>zip</code> 和 <code>TextRecordInputStream</code>。</p></li></ul><h2 id="touchz"><a href="#touchz" class="headerlink" title="touchz"></a>touchz</h2><ul><li><p><strong>使用方法</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop fs -touchz URI [URI …]</span><br></pre></td></tr></table></figure></p></li><li><p><strong>说明</strong>：创建一个 0 字节的空文件。</p></li><li><p><strong>示例</strong>：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hadoop -touchz pathname</span><br></pre></td></tr></table></figure></p></li><li><p><strong>返回值</strong>：成功返回 <code>0</code>，失败返回 <code>-1</code>。</p></li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/Hadoop/">Hadoop</category>
      
      
      <comments>https://blog.eurkon.com/post/d1e7dfd3.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Git 使用教程</title>
      <link>https://blog.eurkon.com/post/c3cf24c7.html</link>
      <guid>https://blog.eurkon.com/post/c3cf24c7.html</guid>
      <pubDate>Sun, 21 Feb 2021 05:16:02 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;什么是-Git&quot;&gt;&lt;a href=&quot;#什么是-Git&quot; class=&quot;headerlink&quot; title=&quot;什么是 Git&quot;&gt;&lt;/a&gt;什么是 Git&lt;/h2&gt;&lt;p&gt;Git 是一个开源的分布式版本控制系统，用于敏捷高效地处理任何或小或大的项目。&lt;/p&gt;
&lt;p&gt;Git</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="什么是-Git"><a href="#什么是-Git" class="headerlink" title="什么是 Git"></a>什么是 Git</h2><p>Git 是一个开源的分布式版本控制系统，用于敏捷高效地处理任何或小或大的项目。</p><p>Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。</p><p>Git 与常用的版本控制工具 CVS，Subversion 等不同，它采用了分布式版本库的方式，不必服务器端软件支持。</p><h2 id="Git-与-SVN-区别"><a href="#Git-与-SVN-区别" class="headerlink" title="Git 与 SVN 区别"></a>Git 与 SVN 区别</h2><p>Git 不仅仅是个版本控制系统，它也是个内容管理系统（CMS），工作管理系统等。</p><p>如果你是一个具有使用 SVN 背景的人，你需要做一定的思想转换，来适应 Git 提供的一些概念和特征。</p><p>Git 与 SVN 区别点：</p><ol><li>Git 是分布式的，SVN 是集中式的：这是 Git 和其它非分布式的版本控制系统，例如 SVN，CVS 等，最核心的区别。</li><li>Git 把内容按元数据方式存储，而 SVN 是按文件：所有的资源控制系统都是把文件的元信息隐藏在一个类似 .svn、.cvs 等的文件夹里。</li><li>Git 分支和 SVN 的分支不同：分支在 SVN 中一点都不特别，其实它就是版本库中的另外一个目录。</li><li>Git 没有一个全局的版本号，而 SVN 有：目前为止这是跟 SVN 相比 Git 缺少的最大的一个特征。</li><li>Git 的内容完整性要优于 SVN：Git 的内容存储使用的是 SHA-1 哈希算法。这能确保代码内容的完整性，确保在遇到磁盘故障和网络问题时降低对版本库的破坏。</li></ol><h2 id="Git-的安装和配置"><a href="#Git-的安装和配置" class="headerlink" title="Git 的安装和配置"></a>Git 的安装和配置</h2><p><a href="https://git-scm.com/downloads">Git 下载地址（Linux/Unix，Mac，Windows 等相关平台）</a></p><p><a href="http://git-scm.com/docs">Git 完整命令手册地址</a></p><p><a href="github-git-cheat-sheet.pdf">Git PDF 版命令手册</a></p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">## 配置所有 Git 仓库的 用户名 和 email</span></span> </span><br><span class="line"><span class="meta">$</span><span class="bash"> git config --global user.name <span class="string">&quot;Your Name&quot;</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git config --global user.email <span class="string">&quot;youremail@example.com&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">## 配置当前 Git 仓库的 用户名 和 email</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git config user.name <span class="string">&quot;Your Name&quot;</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git config user.email <span class="string">&quot;youremail@example.com&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">## 查看全局配置的 用户名 和 email</span></span> </span><br><span class="line"><span class="meta">$</span><span class="bash"> git config --global user.name      查看用户名</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git config --global user.email     查看邮箱地址</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">## 查看当前仓库配置的 用户名 和 email</span></span> </span><br><span class="line"><span class="meta">$</span><span class="bash"> git config user.name      查看用户名</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git config user.email     查看邮箱地址</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Git 是分布式版本控制系统，所以，每个机器都配置你的名字和 Email 地址</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> git config 命令的 --global 参数，用了这个参数，表示你这台机器上所有的 Git 仓库都会使用这个配置，当然也可以对某个仓库指定不同的用户名和 Email 地址（不加 --global）。</span></span><br></pre></td></tr></table></figure></p><h2 id="Git-工作流程"><a href="#Git-工作流程" class="headerlink" title="Git 工作流程"></a>Git 工作流程</h2><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/git/workflow.png" alt="工作流程"></p><p>以上包括一些简单而常用的命令，但是先不关心这些，先来了解下面这 4 个专有名词。</p><ul><li>Workspace：<a href="#工作区">工作区</a></li><li>Index/Stage：<a href="#暂存区">暂存区</a></li><li>Repository：<a href="#本地仓库">仓库区（或本地仓库）</a></li><li>Remote：<a href="#远程仓库">远程仓库</a></li></ul><h3 id="工作区"><a href="#工作区" class="headerlink" title="工作区"></a>工作区</h3><p>程序员进行开发改动的地方，是你当前看到的，也是最新的。</p><p>平常我们开发就是拷贝远程仓库中的一个分支，基于该分支进行开发。在开发过程中就是对工作区的操作。</p><h3 id="暂存区"><a href="#暂存区" class="headerlink" title="暂存区"></a>暂存区</h3><p>.git 目录下的 index 文件，暂存区会记录 <code>git add</code> 添加文件的相关信息（文件名、大小、timestamp...），不保存文件实体，通过 id 指向每个文件实体。可以使用 <code>git status</code> 查看暂存区的状态。暂存区标记了你当前工作区中，哪些内容是被 git 管理的。</p><p>当你完成某个需求或功能后需要提交到远程仓库，那么第一步就是通过 <code>git add</code> 先提交到暂存区，被 git 管理。</p><h3 id="本地仓库"><a href="#本地仓库" class="headerlink" title="本地仓库"></a>本地仓库</h3><p>保存了对象被提交过的各个版本，比起工作区和暂存区的内容，它要更旧一些。</p><p><code>git commit</code> 后同步 index 的目录树到本地仓库，方便从下一步通过 <code>git push</code> 同步本地仓库与远程仓库的同步。</p><h3 id="远程仓库"><a href="#远程仓库" class="headerlink" title="远程仓库"></a>远程仓库</h3><p>远程仓库的内容可能被分布在多个地点的处于协作关系的本地仓库修改，因此它可能与本地仓库同步，也可能不同步，但是它的内容是最旧的。</p><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><ul><li>任何对象都是在工作区中诞生和被修改；</li><li>任何修改都是从进入 <code>index</code> 区才开始被版本控制；</li><li>只有把修改提交到本地仓库，该修改才能在仓库中留下痕迹；</li><li>与协作者分享本地的修改，可以把它们 <code>push</code> 到远程仓库来共享。</li></ul><p>下面这幅图更加直接阐述了四个区域之间的关系，可能有些命令不太清楚，下面将会详细介绍。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/git/workflow2.png" alt="工作流程"></p><h2 id="Git-常用命令"><a href="#Git-常用命令" class="headerlink" title="Git 常用命令"></a>Git 常用命令</h2><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/git/command.png" alt="常用命令"></p><h3 id="版本与配置"><a href="#版本与配置" class="headerlink" title="版本与配置"></a>版本与配置</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git                         查看 git 的相关命令（git --<span class="built_in">help</span>）</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git --version               查看 git 的版本</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git config                  查看 git config 的相关命令</span></span><br></pre></td></tr></table></figure></p><h3 id="初始化本地仓库"><a href="#初始化本地仓库" class="headerlink" title="初始化本地仓库"></a>初始化本地仓库</h3><p>Git 使用 <code>git init</code> 命令来初始化一个 Git 仓库，Git 的很多命令都需要在 Git 的仓库中运行，所以 <code>git init</code> 是使用 Git 的第一个命令。</p><p>在执行完成 <code>git init</code> 命令后，Git 仓库会生成一个 .git 目录，该目录包含了资源的所有元数据，其他的项目目录保持不变。</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git init                    创建本地仓库</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">clone</span>                   克隆远程仓库</span></span><br></pre></td></tr></table></figure></p><h3 id="添加文件到仓库"><a href="#添加文件到仓库" class="headerlink" title="添加文件到仓库"></a>添加文件到仓库</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git add &lt;file&gt;                 如: git add readme.txt</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git commit -m <span class="string">&quot;description&quot;</span>    如: git commit -m <span class="string">&quot;add readme.txt&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 添加文件到仓库分两步:</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 1. add 添加该文件到仓库</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 添加许多同种类型的文件，可以使用通配符 *（记得加引号），如: git add <span class="string">&quot;*.txt&quot;</span> 命令就是添加所有 .txt 文件</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 2. commit 提交该文件到仓库，description 为你对该次提交的描述说明</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 注意: 可以多次 add 不同的文件，commit 可以一次提交多个文件</span></span><br></pre></td></tr></table></figure></p><h3 id="查看仓库目前状态"><a href="#查看仓库目前状态" class="headerlink" title="查看仓库目前状态"></a>查看仓库目前状态</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git status                 查看项目是否有修改、添加、未追踪的文件等</span></span><br></pre></td></tr></table></figure></p><h3 id="查看修改"><a href="#查看修改" class="headerlink" title="查看修改"></a>查看修改</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git diff                   查看工作区（work dict）和暂存区（stage）的区别</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git diff &lt;file&gt;</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git diff --cached          查看暂存区（stage）和分支（master）的区别</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git diff HEAD -- &lt;file&gt;    查看工作区和版本库里面最新版本的区别</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash">如: git diff readme.txt     查看 readme.txt 修改了什么，有什么不同</span></span><br></pre></td></tr></table></figure></p><h3 id="查看提交日志"><a href="#查看提交日志" class="headerlink" title="查看提交日志"></a>查看提交日志</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">log</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">log</span> --oneline           美化输出信息，每个记录显示为一行，显示 commit_id 前几位数</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">log</span> --pretty=oneline    美化输出信息，每个记录显示为一行，显示完整的 commit_id</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">log</span> --graph --pretty=format:<span class="string">&#x27;%h -%d %s (%cr)&#x27;</span> --abbrev-commit --</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">log</span> --graph --pretty=oneline --abbrev-commit</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 显示从最近到最远的提交日志</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 日志输出一大串类似 3628164...882e1e0 的是 commit_id（版本号），和 SVN 不一样，Git 的 commit_id 不是 1，2，3... 递增的数字，而是一个 SHA1 计算出来的一个非常大的数字，用十六进制表示，因为 Git 是分布式的版本控制系统，当多人在同一个版本库里工作，如果大家都用 1，2，3... 作为版本号，那肯定就冲突了</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 最后一个会打印出提交的时间等，（HEAD -&gt; master）指向的是当前的版本</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 退出查看 <span class="built_in">log</span> 日志，输入字母 q（英文状态）</span></span><br></pre></td></tr></table></figure></p><h3 id="版本回退"><a href="#版本回退" class="headerlink" title="版本回退"></a>版本回退</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git reset --hard HEAD^</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git reset --hard &lt;commit_id&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> HEAD        表示当前版本，也就是最新的提交</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> HEAD^       上一个版本</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> HEAD^^      上上一个版本</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> HEAD~100    往上 100 个版本</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 回退到 commit_id 对应的那个版本，commit_id 为版本号，只需要前几位就行</span></span><br></pre></td></tr></table></figure></p><h3 id="查看命令历史"><a href="#查看命令历史" class="headerlink" title="查看命令历史"></a>查看命令历史</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git reflog</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 假如我们依次提交了三个版本 a-&gt;b-&gt;c，然后昨天我们从版本 c 回退到了版本 b，今天我们又想要回到版本 c，此时就可以使用 reflog 命令来查找 c 版本的 commit_id，然后使用 reset 命令来进行版本回退</span></span><br></pre></td></tr></table></figure></p><h3 id="撤销修改"><a href="#撤销修改" class="headerlink" title="撤销修改"></a>撤销修改</h3><p><strong>丢弃工作区（Working Directory）的修改</strong></p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git restore &lt;file&gt;        （建议使用，如: git restore readme.txt）</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git checkout -- &lt;file&gt;</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 命令中 -- 很重要，没有就变成 “切换到另一个分支” 的命令</span></span><br></pre></td></tr></table></figure></p><p><strong>丢弃暂存区（stage/index）的修改</strong></p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 第一步: 把暂存区的修改撤销掉（unstage），重新放回工作区</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git restore --staged &lt;file&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 第二步: 撤销工作区的修改</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git restore &lt;file&gt;</span></span><br></pre></td></tr></table></figure></p><p><strong>小结</strong></p><ul><li><p>当你改乱了工作区某个文件的内容，想直接丢弃工作区的修改时，用命令 <code>git restore &lt;file&gt;</code>。</p></li><li><p>当你不但改乱了工作区某个文件的内容，还添加到了暂存区时，想丢弃修改，分两步，第一步用命令 <code>git restore --staged &lt;file&gt;</code>，就回到了场景 1，第二步按场景 1 操作。</p></li><li><p>已经提交了不合适的修改到版本库时，想要撤销本次提交，参考<a href="#版本回退">版本回退</a>一节，不过前提是没有推送到远程库。</p></li></ul><h3 id="删除文件"><a href="#删除文件" class="headerlink" title="删除文件"></a>删除文件</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git rm &lt;file&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> git rm &lt;file&gt; 相当于执行</span></span><br><span class="line">- rm &lt;file&gt;</span><br><span class="line">- git add &lt;file&gt;</span><br></pre></td></tr></table></figure></p><h2 id="远程仓库-1"><a href="#远程仓库-1" class="headerlink" title="远程仓库"></a>远程仓库</h2><ol><li><p>创建 SSH Key</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> ssh-keygen -t rsa -C <span class="string">&quot;youremail@example.com&quot;</span></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 邮件地址换成你自己的邮件地址，然后一直回车，使用默认值即可，无需设置密码。</span></span><br></pre></td></tr></table></figure></p><p> 在用户主目录下，看看有没有 .ssh 目录，如果有，再看看这个目录下有没有 id_rsa 和 id_rsa.pub 这两个文件，如果已经有了，可直接跳到下一步。如果没有，创建 SSH Key</p><p> 如果一切顺利的话，可以在用户主目录里找到 .ssh 目录，里面有 id_rsa 和 id_rsa.pub 两个文件，这两个就是 SSH Key 的秘钥对，id_rsa 是私钥，不能泄露出去，id_rsa.pub 是公钥，可以放心地告诉任何人。</p></li><li><p>登录 GitHub，在 Settings 中找到 SSH 设置项中添加新的 SSH Key，设置任意 title，在 Key 文本框里粘贴 id_rsa.pub 文件的内容</p></li><li><p>关联远程仓库（先有本地仓库）</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git remote add origin git@github.com:username/repo.git</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 后面的地址换成自己的 GitHub 仓库地址</span></span><br></pre></td></tr></table></figure></p></li><li><p>推送到远程仓库</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git remote       查看远程库信息</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git remote -v    查看远程库详细信息</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git remote rm origin  删除已关联的远程库 origin</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git push -u origin master    <span class="comment">#第一次推送</span></span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git push origin master      推送本地 master 分支到远程库</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git push origin dev         推送本地 dev 分支到远程库</span></span><br><span class="line"><span class="meta">#</span><span class="bash">  除了第一次推送，不需要添加 -u 参数</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 一个本地库关联多个远程库，例如同时关联 GitHub 和 Gitee:</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 1. 先关联 GitHub 的远程库：（注意:远程库的名称叫 github，不叫 origin）</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git remote add github git@github.com:username/repo.git</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 2. 再关联 Gitee 的远程库：（注意:远程库的名称叫 gitee，不叫 origin）</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git remote add gitee git@gitee.com:username/repo.git</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 3. 推送到远程库</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git push github master</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git push gitee master</span></span><br></pre></td></tr></table></figure></p><p> 加上了 <code>-u</code> 参数，Git 不但会把本地的 master 分支内容推送的远程新的 master 分支，还会把本地的 master 分支和远程的 master 分支关联起来</p></li><li><p>从远程仓库克隆（先有远程库）</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">clone</span> git@github.com:username/repo.git</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> GitHub 支持多种协议，上面是 ssh 协议，还有 https 协议</span></span><br></pre></td></tr></table></figure></p></li></ol><h2 id="Git-分支"><a href="#Git-分支" class="headerlink" title="Git 分支"></a>Git 分支</h2><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git branch           查看分支列表及当前分支</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git branch dev       创建 dev 分支</span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> git switch dev       切换到 dev 分支（git checkout dev）</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git switch -c dev    创建并切换到新的 dev 分支（git checkout -b dev）</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git switch -c dev origin/dev  创建远程 origin 的 dev 分支到本地并切换到该分支</span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> git branch -d dev    删除 dev 分支</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git branch -D dev    强制删除 dev 分支</span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> git merge dev        合并 dev 分支到当前分支（当有冲突的时候，需要先解决冲突）</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git merge --no-ff -m <span class="string">&quot;merge with no-ff&quot;</span> dev    合并 dev 分支到当前分支（禁用 Fast forward 合并策略）</span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> git pull  拉取远程分支最新的内容</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git branch --set-upstream-to=origin/dev dev    指定本地 dev 分支与远程 origin/dev 分支的链接</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 为本次合并要创建一个新的 commit，所以加上 -m 参数，把 commit 描述写进去</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 合并分支时，加上 --no-ff 参数就可以用普通模式合并，合并后的历史有分支，能看出来曾经做过合并，而 fast forward 合并就看不出来曾经做过合并</span></span><br></pre></td></tr></table></figure></p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">log</span> --graph    查看分支合并图</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git <span class="built_in">log</span> --graph --pretty=oneline --abbrev-commit</span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> git stash          保存当前工作区和暂存区的修改状态，切换到其他分支修复 bug 等工作，然后在回来继续工作</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git stash list     查看保存现场的列表</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git stash pop      恢复的同时把 stash 内容也删除</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git stash apply    恢复现场，stash 内容并不删除</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git stash drop     删除 stash 内容</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git stash apply stash@&#123;0&#125;    多次 stash，恢复的时候，先用 git stash list 查看，然后恢复指定的 stash</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 通常在 dev 分支开发时，需要有紧急 bug 需要马上处理，保存现在修改的文件等，先修复 bug 后再回来继续工作的情况</span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> git cherry-pick &lt;commit&gt;    复制一个特定的提交到当前分支（当前分支的内容需要先 commit，然后冲突的文件需要解决冲突，然后 commit）</span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> git rebase    把本地未 push 的分叉提交历史整理成直线（使得我们在查看历史提交的变化时更容易，因为分叉的提交需要三方对比）</span></span><br></pre></td></tr></table></figure></p><h2 id="Git-标签"><a href="#Git-标签" class="headerlink" title="Git 标签"></a>Git 标签</h2><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 切换到对应的分支 branch 上，查看或者操作对应的标签 tag</span> </span><br><span class="line"><span class="meta">$</span><span class="bash"> git tag                                 查看所有的标签</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git tag &lt;tagname&gt;                       打标签（默认标签是打在最新提交的 commit 上），如: git tag v1.0</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git tag &lt;tagname&gt; &lt;commit_id&gt;           给对应的 commit_id 打标签</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git tag -a &lt;tagname&gt; -m <span class="string">&quot;标签说明信息&quot;</span> &lt;commit_id&gt;    创建带有说明的标签，用 -a 指定标签名，-m 指定说明文字</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git tag -d &lt;tagname&gt;                    删除一个本地标签</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git push origin :refs/tags/&lt;tagname&gt;    可以删除一个远程标签</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git show &lt;tagname&gt;                      查看标签信息</span></span><br><span class="line"></span><br><span class="line"><span class="meta">$</span><span class="bash"> git push origin &lt;tagname&gt;               推送一个本地标签到远程</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git push origin --tags                  一次性推送全部尚未推送到远程的本地标签</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 删除远程标签，需要先删除本地标签，然后在删除远程标签，如:删除标签 v0.9</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git tag -d v0.9</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> git push origin :refs/tags/v0.9</span></span><br></pre></td></tr></table></figure></p><h2 id="Commit-message-格式"><a href="#Commit-message-格式" class="headerlink" title="Commit message 格式"></a>Commit message 格式</h2><p>Git 每次提交代码，都要写 Commit message（提交说明），否则就不允许提交。但是，一般来说，Commit message 应该清晰明了，说明本次提交的目的。</p><p>每次提交，Commit message 都包括三个部分：header，body 和 footer。</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">type</span>&gt;</span></span>(<span class="xml"><span class="tag">&lt;<span class="name">scope</span>&gt;</span></span>): <span class="xml"><span class="tag">&lt;<span class="name">subject</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">BLANK</span> <span class="attr">LINE</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">BLANK</span> <span class="attr">LINE</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">footer</span>&gt;</span></span></span><br></pre></td></tr></table></figure></p><p>其中，header 是必需的，body 和 footer 可以省略。</p><p>不管是哪一个部分，任何一行都不得超过 72 个字符（或 100 个字符）。这是为了避免自动换行影响美观。</p><h3 id="Header"><a href="#Header" class="headerlink" title="Header"></a>Header</h3><p>Header 部分只有一行，包括三个字段：<code>type</code>（必需）、<code>scope</code>（可选）和<code>subject</code>（必需）。</p><h4 id="type"><a href="#type" class="headerlink" title="type"></a>type</h4><p>用于说明 commit 的类别，只允许使用下面 7 个标识。</p><ul><li><code>feat</code>：新功能（feature）</li><li><code>fix</code>：修补 bug</li><li><code>docs</code>：文档（documentation）</li><li><code>style</code>：格式（不影响代码运行的变动）</li><li><code>refactor</code>：重构（即不是新增功能，也不是修改 bug 的代码变动）</li><li><code>test</code>：增加测试</li><li><code>chore</code>：构建过程或辅助工具的变动</li></ul><p>如果 type 为 <code>feat</code> 和 <code>fix</code>，则该 commit 将肯定出现在 Change log 之中。其他情况（docs、chore、style、refactor、test）由你决定，要不要放入 Change log，建议是不要。</p><h4 id="scope"><a href="#scope" class="headerlink" title="scope"></a>scope</h4><p>scope 用于说明 commit 影响的范围，比如数据层、控制层、视图层等等，视项目不同而不同。</p><p>例如在 Angular，可以是 <code>$location</code>, <code>$browser</code>, <code>$compile</code>, <code>$rootScope</code>, <code>ngHref</code>, <code>ngClick</code>, <code>ngView</code> 等。</p><p>如果你的修改影响了不止一个 <code>scope</code>，你可以使用 <code>*</code> 代替。</p><h4 id="subject"><a href="#subject" class="headerlink" title="subject"></a>subject</h4><p>subject 是 commit 目的的简短描述，不超过 50 个字符。</p><p>其他注意事项：</p><ul><li>以动词开头，使用第一人称现在时，比如 change，而不是 changed 或 changes</li><li>第一个字母小写</li><li>结尾不加句号（.）</li></ul><h3 id="Body"><a href="#Body" class="headerlink" title="Body"></a>Body</h3><p>Body 部分是对本次 commit 的详细描述，可以分成多行。下面是一个范例。</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">More detailed explanatory text, if necessary.  Wrap it to about 72 characters or so. </span><br><span class="line"></span><br><span class="line">Further paragraphs come after blank lines.</span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> Bullet points are okay, too</span><br><span class="line"><span class="bullet">-</span> Use a hanging indent</span><br></pre></td></tr></table></figure></p><p>有三个注意点:</p><ul><li>使用第一人称现在时，比如使用 change 而不是 changed 或 changes。</li><li>永远别忘了第 2 行是空行</li><li>应该说明代码变动的动机，以及与以前行为的对比。</li></ul><h3 id="Footer"><a href="#Footer" class="headerlink" title="Footer"></a>Footer</h3><p>Footer 部分只用于以下两种情况：</p><h4 id="不兼容变动"><a href="#不兼容变动" class="headerlink" title="不兼容变动"></a>不兼容变动</h4><p>如果当前代码与上一个版本不兼容，则 Footer 部分以 <code>BREAKING CHANGE</code> 开头，后面是对变动的描述、以及变动理由和迁移方法。</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">BREAKING CHANGE: isolate scope bindings definition has changed.</span><br><span class="line"></span><br><span class="line"><span class="code">    To migrate the code follow the example below:</span></span><br><span class="line"><span class="code"></span></span><br><span class="line"><span class="code">    Before:</span></span><br><span class="line"><span class="code"></span></span><br><span class="line"><span class="code">    scope: &#123;</span></span><br><span class="line"><span class="code">      myAttr: &#x27;attribute&#x27;,</span></span><br><span class="line"><span class="code">    &#125;</span></span><br><span class="line"><span class="code"></span></span><br><span class="line"><span class="code">    After:</span></span><br><span class="line"><span class="code"></span></span><br><span class="line"><span class="code">    scope: &#123;</span></span><br><span class="line"><span class="code">      myAttr: &#x27;@&#x27;,</span></span><br><span class="line"><span class="code">    &#125;</span></span><br><span class="line"><span class="code"></span></span><br><span class="line"><span class="code">    The removed `inject` wasn&#x27;t generaly useful for directives so there should be no code using it.</span></span><br></pre></td></tr></table></figure></p><h4 id="关闭-Issue"><a href="#关闭-Issue" class="headerlink" title="关闭 Issue"></a>关闭 Issue</h4><p>如果当前 commit 针对某个 issue，那么可以在 Footer 部分关闭这个 issue 。</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Closes #234</span><br></pre></td></tr></table></figure></p><h3 id="Revert"><a href="#Revert" class="headerlink" title="Revert"></a>Revert</h3><p>还有一种特殊情况，如果当前 commit 用于撤销以前的 commit，则必须以 <code>revert:</code> 开头，后面跟着被撤销 Commit 的 Header。</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">revert: feat(pencil): add &#x27;graphiteWidth&#x27; option</span><br><span class="line"></span><br><span class="line">This reverts commit 667ecc1654a317a13331b17617d973392f415f02.</span><br></pre></td></tr></table></figure></p><p>Body 部分的格式是固定的，必须写成 <code>This reverts commit &amp;lt;hash&gt;.</code>，其中的 hash 是被撤销 commit 的 SHA 标识符。</p><p>如果当前 commit 与被撤销的 commit，在同一个发布（release）里面，那么它们都不会出现在 Change log 里面。如果两者在不同的发布，那么当前 commit，会出现在 Change log 的 Reverts 小标题下面。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Git/">Git</category>
      
      
      <comments>https://blog.eurkon.com/post/c3cf24c7.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>26 个字母 26 句话</title>
      <link>https://blog.eurkon.com/post/32c40e7c.html</link>
      <guid>https://blog.eurkon.com/post/32c40e7c.html</guid>
      <pubDate>Fri, 12 Feb 2021 00:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;style&gt;#article-container img { width: 33%; display: inline-block;}&lt;/style&gt;

&lt;/p&gt;
&lt;p&gt;一个字母一张图，一张图一个感悟，一个感悟一份思考，一份思考一个行动。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;</description>
        
      
      
      
      <content:encoded><![CDATA[<p><style>#article-container img { width: 33%; display: inline-block;}</style></p><p>一个字母一张图，一张图一个感悟，一个感悟一份思考，一份思考一个行动。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/a_proverb.png" alt="A"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/b_proverb.png" alt="B"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/c_proverb.png" alt="C"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/d_proverb.png" alt="D"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/e_proverb.png" alt="E"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/f_proverb.png" alt="F"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/g_proverb.png" alt="G"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/h_proverb.png" alt="H"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/i_proverb.png" alt="I"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/j_proverb.png" alt="J"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/k_proverb.png" alt="K"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/l_proverb.png" alt="L"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/m_proverb.png" alt="M"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/n_proverb.png" alt="N"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/o_proverb.png" alt="O"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/p_proverb.png" alt="P"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/q_proverb.png" alt="Q"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/r_proverb.png" alt="R"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/s_proverb.png" alt="S"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/t_proverb.png" alt="T"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/u_proverb.png" alt="U"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/v_proverb.png" alt="V"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/w_proverb.png" alt="W"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/x_proverb.png" alt="X"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/y_proverb.png" alt="Y"><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/letter/z_proverb.png" alt="Z"></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E7%94%9F%E6%B4%BB%E9%9A%8F%E7%AC%94/">生活随笔</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E7%94%9F%E6%B4%BB%E9%9A%8F%E7%AC%94/">生活随笔</category>
      
      
      <comments>https://blog.eurkon.com/post/32c40e7c.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>R 语言统计分析</title>
      <link>https://blog.eurkon.com/post/741c153c.html</link>
      <guid>https://blog.eurkon.com/post/741c153c.html</guid>
      <pubDate>Fri, 22 Jan 2021 02:10:41 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;R 中的统计分析通过使用许多内置函数来执行。这些函数大多数是 R 基础包的一部分。这些函数将 R 向量作为输入和参数，并给出结果。&lt;/p&gt;
</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>R 中的统计分析通过使用许多内置函数来执行。这些函数大多数是 R 基础包的一部分。这些函数将 R 向量作为输入和参数，并给出结果。</p><h2 id="平均值"><a href="#平均值" class="headerlink" title="平均值"></a>平均值</h2><p>通过求出数据集的和再除以求和数的总量得到平均值</p><p>函数 <code>mean()</code> 用于在 R 语言中计算平均值。</p><h3 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h3><p>用于计算 R 中的平均值的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mean(x, trim = <span class="number">0</span>, na.rm = <span class="literal">FALSE</span>, ...)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是输入向量。</li><li><code>trim</code> 用于从排序向量的两端丢弃一些观察结果。</li><li><code>na.rm</code> 用于从输入向量中删除缺失值。</li></ul><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a vector. </span></span><br><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">12</span>, <span class="number">7</span>, <span class="number">3</span>, <span class="number">4.2</span>, <span class="number">18</span>, <span class="number">2</span>, <span class="number">54</span>, -<span class="number">21</span>, <span class="number">8</span>, -<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Find Mean.</span></span><br><span class="line">result.mean &lt;- mean(x)</span><br><span class="line">print(result.mean)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">8.22</span></span><br></pre></td></tr></table></figure></p><h3 id="应用-trim-选项"><a href="#应用-trim-选项" class="headerlink" title="应用 trim 选项"></a>应用 trim 选项</h3><p>当提供 <code>trim</code> 参数时，向量中的值被排序，然后从计算平均值中减去所需的观察值。</p><p>当 <code>trim = 0.3</code> 时，来自每端的 3 个值将从计算中减去以找到均值。</p><p>在这种情况下，排序的向量是 <code>(-21, -5, 2, 3, 4.2, 7, 8, 12, 18, 54)</code>，并且从用于计算平均值的向量中移除的值是左边的 <code>(-21, -5, 2)</code> 从和右边的 <code>(12, 18, 54)</code>。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a vector.</span></span><br><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">12</span>, <span class="number">7</span>, <span class="number">3</span>, <span class="number">4.2</span>, <span class="number">18</span>, <span class="number">2</span>, <span class="number">54</span>, -<span class="number">21</span>, <span class="number">8</span>, -<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Find Mean.</span></span><br><span class="line">result.mean &lt;- mean(x, trim = <span class="number">0.3</span>)</span><br><span class="line">print(result.mean)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">5.55</span></span><br></pre></td></tr></table></figure></p><h3 id="应用-NA-选项"><a href="#应用-NA-选项" class="headerlink" title="应用 NA 选项"></a>应用 NA 选项</h3><p>如果有缺失值，则平均函数返回 <code>NA</code>。</p><p>要从计算中删除缺少的值，请使用 <code>na.rm = TRUE</code>。这意味着去除 <code>NA</code> 值。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a vector. </span></span><br><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">12</span>, <span class="number">7</span>, <span class="number">3</span>, <span class="number">4.2</span>, <span class="number">18</span>, <span class="number">2</span>, <span class="number">54</span>, -<span class="number">21</span>, <span class="number">8</span>, -<span class="number">5</span>, <span class="literal">NA</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Find mean.</span></span><br><span class="line">result.mean &lt;- mean(x)</span><br><span class="line">print(result.mean)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Find mean dropping NA values.</span></span><br><span class="line">result.mean &lt;- mean(x, na.rm = <span class="literal">TRUE</span>)</span><br><span class="line">print(result.mean)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="literal">NA</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">8.22</span></span><br></pre></td></tr></table></figure></p><h2 id="中位数"><a href="#中位数" class="headerlink" title="中位数"></a>中位数</h2><p>数据系列中的最中间值称为中位数。在 R 语言中使用 <code>median()</code> 函数来计算此值。</p><h3 id="语法-1"><a href="#语法-1" class="headerlink" title="语法"></a>语法</h3><p>计算 R 语言中位数的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">median(x, na.rm = <span class="literal">FALSE</span>)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是输入向量。</li><li><code>na.rm</code> 用于从输入向量中删除缺失值。</li></ul><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the vector.</span></span><br><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">12</span>, <span class="number">7</span>, <span class="number">3</span>, <span class="number">4.2</span>, <span class="number">18</span>, <span class="number">2</span>, <span class="number">54</span>, -<span class="number">21</span>, <span class="number">8</span>, -<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Find the median.</span></span><br><span class="line">median.result &lt;- median(x)</span><br><span class="line">print(median.result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">5.6</span></span><br></pre></td></tr></table></figure></p><h2 id="众数"><a href="#众数" class="headerlink" title="众数"></a>众数</h2><p>众数是一组数据中出现次数最多的值，不同于平均值和中位数，众数可以同时包含数字和字符数据。R 没有标准的内置函数来计算众数。因此，我们将创建一个用户自定义函数来计算 R 语言中的数据集的众数。该函数将向量作为输入，并将众数值作为输出。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the function.</span></span><br><span class="line">getmode &lt;- <span class="keyword">function</span>(v) &#123;</span><br><span class="line">   uniqv &lt;- unique(v)</span><br><span class="line">   uniqv[which.max(tabulate(match(v, uniqv)))]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the vector with numbers.</span></span><br><span class="line">v &lt;- <span class="built_in">c</span>(<span class="number">2</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">5</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Calculate the mode using the user function.</span></span><br><span class="line">result &lt;- getmode(v)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the vector with characters.</span></span><br><span class="line">charv &lt;- <span class="built_in">c</span>(<span class="string">&quot;o&quot;</span>, <span class="string">&quot;it&quot;</span>, <span class="string">&quot;the&quot;</span>, <span class="string">&quot;it&quot;</span>, <span class="string">&quot;it&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Calculate the mode using the user function.</span></span><br><span class="line">result &lt;- getmode(charv)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">2</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;it&quot;</span></span><br></pre></td></tr></table></figure></p><h2 id="线性回归"><a href="#线性回归" class="headerlink" title="线性回归"></a>线性回归</h2><p>回归分析是一种非常广泛使用的统计工具，用于建立两个变量之间的关系模型。这些变量之一称为预测变量，其值通过实验收集。另一个变量称为响应变量，其值从预测变量派生。</p><p>在线性回归中，这两个变量通过方程相关，其中这两个变量的指数（幂）为 1，数学上，线性关系表示当绘制为曲线图时的直线。任何变量的指数不等于 1 的非线性关系将创建一条曲线。</p><p>线性回归的一般数学方程为：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">y = ax + b</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>y</code> 是响应变量。</li><li><code>x</code> 是预测变量。</li><li><code>a，b</code> 被称为系数常数。</li></ul><p><strong>建立回归的步骤</strong></p><p>回归的简单例子是当人的身高已知时预测人的体重。为了做到这一点，我们需要有一个人的身高和体重之间的关系。</p><p>创建关系的步骤是：</p><ol><li>进行收集高度和相应重量的观测值的样本的实验。</li><li>使用 R 语言中的 <code>lm()</code> 函数创建关系模型。</li><li>从创建的模型中找到系数，并使用这些创建数学方程</li><li>获得关系模型的摘要以了解预测中的平均误差。也称为残差。</li><li>为了预测新人的体重，使用 R 中的 <code>predict()</code> 函数。</li></ol><p><strong>输入数据</strong></p><p>下面是代表观察的样本数据：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Values of height</span></span><br><span class="line">151, <span class="number">174</span>, <span class="number">138</span>, <span class="number">186</span>, <span class="number">128</span>, <span class="number">136</span>, <span class="number">179</span>, <span class="number">163</span>, <span class="number">152</span>, <span class="number">131</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Values of weight.</span></span><br><span class="line">63, <span class="number">81</span>, <span class="number">56</span>, <span class="number">91</span>, <span class="number">47</span>, <span class="number">57</span>, <span class="number">76</span>, <span class="number">72</span>, <span class="number">62</span>, <span class="number">48</span></span><br></pre></td></tr></table></figure></p><h3 id="lm-函数"><a href="#lm-函数" class="headerlink" title="lm()函数"></a>lm()函数</h3><p>此函数创建预测变量和响应变量之间的关系模型。</p><p><strong>语法</strong></p><p>线性回归中 <code>lm()</code> 函数的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lm(formula, data)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>formula</code> 是表示 <code>x</code> 和 <code>y</code> 之间的关系的符号。</li><li><code>data</code> 是应用公式的向量。</li></ul><p><strong>创建关系模型并获取系数</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">151</span>, <span class="number">174</span>, <span class="number">138</span>, <span class="number">186</span>, <span class="number">128</span>, <span class="number">136</span>, <span class="number">179</span>, <span class="number">163</span>, <span class="number">152</span>, <span class="number">131</span>)</span><br><span class="line">y &lt;- <span class="built_in">c</span>(<span class="number">63</span>, <span class="number">81</span>, <span class="number">56</span>, <span class="number">91</span>, <span class="number">47</span>, <span class="number">57</span>, <span class="number">76</span>, <span class="number">72</span>, <span class="number">62</span>, <span class="number">48</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Apply the lm() function.</span></span><br><span class="line">relation &lt;- lm(y ~ x)</span><br><span class="line"></span><br><span class="line">print(relation)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">Call:</span><br><span class="line">lm(formula = y ~ x)</span><br><span class="line"></span><br><span class="line">Coefficients:</span><br><span class="line">(Intercept)            x</span><br><span class="line">   -<span class="number">38.4551</span>       <span class="number">0.6746</span></span><br></pre></td></tr></table></figure></p><p><strong>获取相关的摘要</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">151</span>, <span class="number">174</span>, <span class="number">138</span>, <span class="number">186</span>, <span class="number">128</span>, <span class="number">136</span>, <span class="number">179</span>, <span class="number">163</span>, <span class="number">152</span>, <span class="number">131</span>)</span><br><span class="line">y &lt;- <span class="built_in">c</span>(<span class="number">63</span>, <span class="number">81</span>, <span class="number">56</span>, <span class="number">91</span>, <span class="number">47</span>, <span class="number">57</span>, <span class="number">76</span>, <span class="number">72</span>, <span class="number">62</span>, <span class="number">48</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Apply the lm() function.</span></span><br><span class="line">relation &lt;- lm(y ~ x)</span><br><span class="line"></span><br><span class="line">print(summary(relation))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">Call:</span><br><span class="line">lm(formula = y ~ x)</span><br><span class="line"></span><br><span class="line">Residuals:</span><br><span class="line">    Min      <span class="number">1</span>Q  Median      <span class="number">3</span>Q     Max </span><br><span class="line">-<span class="number">6.3002</span> -<span class="number">1.6629</span>  <span class="number">0.0412</span>  <span class="number">1.8944</span>  <span class="number">3.9775</span> </span><br><span class="line"></span><br><span class="line">Coefficients:</span><br><span class="line">             Estimate Std. Error t value Pr(&gt;|t|)    </span><br><span class="line">(Intercept) -<span class="number">38.45509</span>    <span class="number">8.04901</span>  -<span class="number">4.778</span>  <span class="number">0.00139</span> ** </span><br><span class="line">x             <span class="number">0.67461</span>    <span class="number">0.05191</span>  <span class="number">12.997</span> <span class="number">1.16e-06</span> ***</span><br><span class="line">---</span><br><span class="line">Signif. codes:  <span class="number">0</span> ‘***’ <span class="number">0.001</span> ‘**’ <span class="number">0.01</span> ‘*’ <span class="number">0.05</span> ‘.’ <span class="number">0.1</span> ‘ ’ <span class="number">1</span></span><br><span class="line"></span><br><span class="line">Residual standard error: <span class="number">3.253</span> on <span class="number">8</span> degrees of freedom</span><br><span class="line">Multiple R-squared:  <span class="number">0.9548</span>,Adjusted R-squared:  <span class="number">0.9491</span> </span><br><span class="line"><span class="built_in">F</span>-statistic: <span class="number">168.9</span> on <span class="number">1</span> and <span class="number">8</span> DF,  p-value: <span class="number">1.164e-06</span></span><br></pre></td></tr></table></figure></p><h3 id="predict-函数"><a href="#predict-函数" class="headerlink" title="predict() 函数"></a>predict() 函数</h3><p><strong>语法</strong></p><p>线性回归中的 <code>predict()</code> 的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">predict(object, newdata)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>object</code> 是已使用 <code>lm()</code> 函数创建的公式。</li><li><code>newdata</code> 是包含预测变量的新值的向量。</li></ul><p><strong>预测新人的体重</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># The predictor vector.</span></span><br><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">151</span>, <span class="number">174</span>, <span class="number">138</span>, <span class="number">186</span>, <span class="number">128</span>, <span class="number">136</span>, <span class="number">179</span>, <span class="number">163</span>, <span class="number">152</span>, <span class="number">131</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># The resposne vector.</span></span><br><span class="line">y &lt;- <span class="built_in">c</span>(<span class="number">63</span>, <span class="number">81</span>, <span class="number">56</span>, <span class="number">91</span>, <span class="number">47</span>, <span class="number">57</span>, <span class="number">76</span>, <span class="number">72</span>, <span class="number">62</span>, <span class="number">48</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Apply the lm() function.</span></span><br><span class="line">relation &lt;- lm(y ~ x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Find weight of a person with height 170.</span></span><br><span class="line">a &lt;- data.frame(x = <span class="number">170</span>)</span><br><span class="line">result &lt;- predict(relation, a)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">       <span class="number">1</span> </span><br><span class="line">76.22869 </span><br></pre></td></tr></table></figure></p><p><strong>以图形方式可视化回归</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the predictor and response variable.</span></span><br><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">151</span>, <span class="number">174</span>, <span class="number">138</span>, <span class="number">186</span>, <span class="number">128</span>, <span class="number">136</span>, <span class="number">179</span>, <span class="number">163</span>, <span class="number">152</span>, <span class="number">131</span>)</span><br><span class="line">y &lt;- <span class="built_in">c</span>(<span class="number">63</span>, <span class="number">81</span>, <span class="number">56</span>, <span class="number">91</span>, <span class="number">47</span>, <span class="number">57</span>, <span class="number">76</span>, <span class="number">72</span>, <span class="number">62</span>, <span class="number">48</span>)</span><br><span class="line">relation &lt;- lm(y ~ x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;linear_regression.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the chart.</span></span><br><span class="line">plot(y, x, col = <span class="string">&quot;blue&quot;</span>, main = <span class="string">&quot;Height &amp; Weight Regression&quot;</span>, abline(lm(x ~ y)), cex = <span class="number">1.3</span>, pch = <span class="number">16</span>, xlab = <span class="string">&quot;Weight in Kg&quot;</span>, ylab = <span class="string">&quot;Height in cm&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/linear_regression.png" alt="线性回归"></p><h2 id="多元回归"><a href="#多元回归" class="headerlink" title="多元回归"></a>多元回归</h2><p>多元回归是线性回归到两个以上变量之间的关系的延伸。在简单线性关系中，我们有一个预测变量和一个响应变量，但在多元回归中，我们有多个预测变量和一个响应变量。</p><p>多元回归的一般数学方程为：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">y = a + b1x1 + b2x2 + ... + bnxn</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>y</code> 是响应变量。</li><li><code>a, b1, b2, ..., bn</code> 是系数。</li><li><code>x1, x2, ..., xn</code> 是预测变量。</li></ul><p>我们使用 R 语言中的 <code>lm()</code> 函数创建回归模型。模型使用输入数据确定系数的值。接下来，我们可以使用这些系数来预测给定的一组预测变量的响应变量的值。</p><p><code>lm()</code> 函数函数创建预测变量和响应变量之间的关系模型。</p><p><strong>语法</strong></p><p><code>lm()</code> 函数在多元回归中的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lm(y ~ x1 + x2 + x3 + ..., data)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>y ~ x1 + x2 + x3...</code> 是表示响应变量和预测变量之间的关系的符号。</li><li><code>data</code> 是应用公式的向量。</li></ul><p><strong>实例</strong></p><p><strong>输入数据</strong></p><p>考虑在 R 语言环境中可用的数据集 <code>mtcars</code> 。它给出了每加仑里程 <code>mpg</code>，气缸排量 <code>disp</code>，马力 <code>hp</code>，汽车重量 <code>wt</code> 和一些其他参数的不同汽车模型之间的比较。</p><p>模型的目标是建立 <code>mpg</code> 作为响应变量与 <code>disp</code>，<code>hp</code> 和 <code>wt</code> 作为预测变量之间的关系。为此，我们从 <code>mtcars</code> 数据集中创建这些变量的子集。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">input &lt;- mtcars[, <span class="built_in">c</span>(<span class="string">&quot;mpg&quot;</span>, <span class="string">&quot;disp&quot;</span>, <span class="string">&quot;hp&quot;</span>, <span class="string">&quot;wt&quot;</span>)]</span><br><span class="line">print(head(input))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">                   mpg disp  hp    wt</span><br><span class="line">Mazda RX4         <span class="number">21.0</span>  <span class="number">160</span> <span class="number">110</span> <span class="number">2.620</span></span><br><span class="line">Mazda RX4 Wag     <span class="number">21.0</span>  <span class="number">160</span> <span class="number">110</span> <span class="number">2.875</span></span><br><span class="line">Datsun <span class="number">710</span>        <span class="number">22.8</span>  <span class="number">108</span>  <span class="number">93</span> <span class="number">2.320</span></span><br><span class="line">Hornet <span class="number">4</span> Drive    <span class="number">21.4</span>  <span class="number">258</span> <span class="number">110</span> <span class="number">3.215</span></span><br><span class="line">Hornet Sportabout <span class="number">18.7</span>  <span class="number">360</span> <span class="number">175</span> <span class="number">3.440</span></span><br><span class="line">Valiant           <span class="number">18.1</span>  <span class="number">225</span> <span class="number">105</span> <span class="number">3.460</span></span><br></pre></td></tr></table></figure></p><p><strong>创建关系模型并获取系数</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">input &lt;- mtcars[, <span class="built_in">c</span>(<span class="string">&quot;mpg&quot;</span>, <span class="string">&quot;disp&quot;</span>, <span class="string">&quot;hp&quot;</span>, <span class="string">&quot;wt&quot;</span>)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the relationship model.</span></span><br><span class="line">model &lt;- lm(mpg ~ disp + hp + wt, data = input)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Show the model.</span></span><br><span class="line">print(model)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the Intercept and coefficients as vector elements.</span></span><br><span class="line">cat(<span class="string">&quot;# # # # The Coefficient Values # # # #&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line">a &lt;- coef(model)[<span class="number">1</span>]</span><br><span class="line">print(a)</span><br><span class="line"></span><br><span class="line">Xdisp &lt;- coef(model)[<span class="number">2</span>]</span><br><span class="line">Xhp &lt;- coef(model)[<span class="number">3</span>]</span><br><span class="line">Xwt &lt;- coef(model)[<span class="number">4</span>]</span><br><span class="line"></span><br><span class="line">print(Xdisp)</span><br><span class="line">print(Xhp)</span><br><span class="line">print(Xwt)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">Call:</span><br><span class="line">lm(formula = mpg ~ disp + hp + wt, data = input)</span><br><span class="line"></span><br><span class="line">Coefficients:</span><br><span class="line">(Intercept)         disp           hp           wt  </span><br><span class="line">  <span class="number">37.105505</span>    -<span class="number">0.000937</span>    -<span class="number">0.031157</span>    -<span class="number">3.800891</span> </span><br><span class="line"></span><br><span class="line"><span class="comment"># # # # The Coefficient Values # # # #</span></span><br><span class="line">(Intercept) </span><br><span class="line">   <span class="number">37.10551</span> </span><br><span class="line"></span><br><span class="line">         disp </span><br><span class="line">-<span class="number">0.0009370091</span></span><br><span class="line">         hp </span><br><span class="line">-<span class="number">0.03115655</span> </span><br><span class="line">       wt </span><br><span class="line">-<span class="number">3.800891</span> </span><br></pre></td></tr></table></figure></p><p><strong>创建回归模型的方程</strong></p><p>基于上述截距和系数值，我们创建了数学方程。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Y = a + Xdisp*x1 + Xhp*x2 + Xwt*x3</span><br><span class="line">or</span><br><span class="line">Y = <span class="number">37.105505</span> + (-<span class="number">0.000937</span>)*x1 + (-<span class="number">0.031157</span>)*x2 + (-<span class="number">3.800891</span>)*x3</span><br></pre></td></tr></table></figure></p><p><strong>应用方程预测新值</strong></p><p>当提供一组新的位移，马力和重量值时，我们可以使用上面创建的回归方程来预测里程数。对于 <code>disp = 221</code>，<code>hp = 102</code> 和 <code>wt = 2.91</code> 的汽车，预测里程为：</p><p><code>Y = 37.105505 + (-0.000937)*221 + (-0.031157)*102 + (-3.800891)*2.91 = 22.65982</code></p><p><strong>使用 <code>predict()</code> 函数预测</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">x = data.frame(disp = <span class="number">221</span>, hp = <span class="number">102</span>, wt = <span class="number">2.91</span>)</span><br><span class="line">result = predict(model, x)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">       <span class="number">1</span> </span><br><span class="line">22.65987</span><br></pre></td></tr></table></figure></p><h2 id="逻辑回归"><a href="#逻辑回归" class="headerlink" title="逻辑回归"></a>逻辑回归</h2><p>逻辑回归是回归模型，其中响应变量（因变量）具有诸如 <code>True/False</code> 或 <code>0/1</code> 的分类值。它实际上基于将其与预测变量相关的数学方程测量二元响应的概率作为响应变量的值。</p><p>逻辑回归的一般数学方程为：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">y = <span class="number">1</span>/(<span class="number">1</span> + e^-(a + b1x1 + b2x2 + b3x3 + ...))</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>y</code> 是响应变量。</li><li><code>x</code> 是预测变量。</li><li><code>a</code> 和 <code>b</code> 是作为数字常数的系数。</li></ul><p>用于创建回归模型的函数是 <code>glm()</code> 函数。</p><p><strong>语法</strong></p><p>逻辑回归中 <code>glm()</code> 函数的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">glm(formula, data, family)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>formula</code> 是表示变量之间的关系的符号。</li><li><code>data</code> 是给出这些变量的值的数据集。</li><li><code>family</code> 是 R 语言对象来指定模型的细节。它的值是二项逻辑回归。</li></ul><p><strong>实例</strong></p><p>内置数据集 <code>mtcars</code> 描述具有各种发动机规格的、不同型号的汽车。在 <code>mtcars</code> 数据集中，传输模式（自动或手动）由 <code>am</code> 列描述，它是一个二进制值 <code>0</code> 或 <code>1</code>。我们可以在列 <code>am</code> 和其他 3 列（<code>hp</code>，<code>wt</code> 和 <code>cyl</code>）之间创建逻辑回归模型。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Select some columns form mtcars.</span></span><br><span class="line">input &lt;- mtcars[, <span class="built_in">c</span>(<span class="string">&quot;am&quot;</span>, <span class="string">&quot;cyl&quot;</span>, <span class="string">&quot;hp&quot;</span>, <span class="string">&quot;wt&quot;</span>)]</span><br><span class="line"></span><br><span class="line">print(head(input))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">                  am   cyl  hp    wt</span><br><span class="line">Mazda RX4          <span class="number">1</span>   <span class="number">6</span>    <span class="number">110</span>   <span class="number">2.620</span></span><br><span class="line">Mazda RX4 Wag      <span class="number">1</span>   <span class="number">6</span>    <span class="number">110</span>   <span class="number">2.875</span></span><br><span class="line">Datsun <span class="number">710</span>         <span class="number">1</span>   <span class="number">4</span>     <span class="number">93</span>   <span class="number">2.320</span></span><br><span class="line">Hornet <span class="number">4</span> Drive     <span class="number">0</span>   <span class="number">6</span>    <span class="number">110</span>   <span class="number">3.215</span></span><br><span class="line">Hornet Sportabout  <span class="number">0</span>   <span class="number">8</span>    <span class="number">175</span>   <span class="number">3.440</span></span><br><span class="line">Valiant            <span class="number">0</span>   <span class="number">6</span>    <span class="number">105</span>   <span class="number">3.460</span></span><br></pre></td></tr></table></figure></p><p><strong>创建回归模型</strong></p><p>我们使用 <code>glm()</code> 函数创建回归模型，并得到其摘要进行分析。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">input &lt;- mtcars[, <span class="built_in">c</span>(<span class="string">&quot;am&quot;</span>, <span class="string">&quot;cyl&quot;</span>, <span class="string">&quot;hp&quot;</span>, <span class="string">&quot;wt&quot;</span>)]</span><br><span class="line"></span><br><span class="line">am.data = glm(formula = am ~ cyl + hp + wt, data = input, family = binomial)</span><br><span class="line"></span><br><span class="line">print(summary(am.data))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">Call:</span><br><span class="line">glm(formula = am ~ cyl + hp + wt, family = binomial, data = input)</span><br><span class="line"></span><br><span class="line">Deviance Residuals: </span><br><span class="line">     Min        <span class="number">1</span>Q    Median        <span class="number">3</span>Q       Max  </span><br><span class="line">-<span class="number">2.17272</span>  -<span class="number">0.14907</span>  -<span class="number">0.01464</span>   <span class="number">0.14116</span>   <span class="number">1.27641</span>  </span><br><span class="line"></span><br><span class="line">Coefficients:</span><br><span class="line">            Estimate Std. Error z value Pr(&gt;|z|)  </span><br><span class="line">(Intercept) <span class="number">19.70288</span>    <span class="number">8.11637</span>   <span class="number">2.428</span>   <span class="number">0.0152</span> *</span><br><span class="line">cyl          <span class="number">0.48760</span>    <span class="number">1.07162</span>   <span class="number">0.455</span>   <span class="number">0.6491</span>  </span><br><span class="line">hp           <span class="number">0.03259</span>    <span class="number">0.01886</span>   <span class="number">1.728</span>   <span class="number">0.0840</span> .</span><br><span class="line">wt          -<span class="number">9.14947</span>    <span class="number">4.15332</span>  -<span class="number">2.203</span>   <span class="number">0.0276</span> *</span><br><span class="line">---</span><br><span class="line">Signif. codes:  <span class="number">0</span> ‘***’ <span class="number">0.001</span> ‘**’ <span class="number">0.01</span> ‘*’ <span class="number">0.05</span> ‘.’ <span class="number">0.1</span> ‘ ’ <span class="number">1</span></span><br><span class="line"></span><br><span class="line">(Dispersion parameter <span class="keyword">for</span> binomial family taken to be <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    Null deviance: <span class="number">43.2297</span>  on <span class="number">31</span>  degrees of freedom</span><br><span class="line">Residual deviance:  <span class="number">9.8415</span>  on <span class="number">28</span>  degrees of freedom</span><br><span class="line">AIC: <span class="number">17.841</span></span><br><span class="line"></span><br><span class="line">Number of Fisher Scoring iterations: <span class="number">8</span></span><br></pre></td></tr></table></figure></p><p><strong>结论</strong></p><p>在 <code>summary</code> 中，对于变量 <code>cyl</code> 和 <code>hp</code> ，最后一列中的 <code>P</code> 值大于 <code>0.05</code>，我们认为它们对变量 <code>am</code> 的值有贡献是无关紧要的。只有重量 <code>wt</code> 影响该回归模型中的 <code>am</code> 值。</p><h2 id="标准分布"><a href="#标准分布" class="headerlink" title="标准分布"></a>标准分布</h2><p>在来自独立源的数据的随机集合中，通常观察到数据的分布是正常的。这意味着，在绘制水平轴上的变量值和垂直轴上的值的计数的图形时，我们得到钟形曲线。曲线的中心表示数据集的平均值。在图中，<code>50%</code> 的值位于平均值的左侧，另外 <code>50%</code> 位于图表的右侧。这在统计学中被称为正态分布。</p><p>R 语言有四个内置函数来产生正态分布。它们描述如下：</p><p><strong>语法</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">dnorm(x, mean, sd)</span><br><span class="line">pnorm(x, mean, sd)</span><br><span class="line">qnorm(p, mean, sd)</span><br><span class="line">rnorm(n, mean, sd)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是数字的向量。</li><li><code>P</code> 是概率的向量。</li><li><code>n</code> 是观察的数量（样本大小）。</li><li><code>mean</code> 是样本数据的平均值。它的默认值为 <code>0</code>。</li><li><code>sd</code> 是标准偏差。它的默认值为 <code>1</code>。</li></ul><h3 id="dnorm"><a href="#dnorm" class="headerlink" title="dnorm()"></a>dnorm()</h3><p>该函数给出给定平均值和标准偏差在每个点的概率分布的高度。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a sequence of numbers between -10 and 10 incrementing by 0.1.</span></span><br><span class="line">x &lt;- seq(-<span class="number">10</span>, <span class="number">10</span>, by = <span class="number">.1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Choose the mean as 2.5 and standard deviation as 0.5.</span></span><br><span class="line">y &lt;- dnorm(x, mean = <span class="number">2.5</span>, sd = <span class="number">0.5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;dnorm.png&quot;</span>)</span><br><span class="line"></span><br><span class="line">plot(x, y)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/dnorm.png" alt="dnorm"></p><h3 id="pnorm"><a href="#pnorm" class="headerlink" title="pnorm()"></a>pnorm()</h3><p>该函数给出正态分布随机数的概率小于给定数的值。它也被称为“累积分布函数”。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a sequence of numbers between -10 and 10 incrementing by 0.2.</span></span><br><span class="line">x &lt;- seq(-<span class="number">10</span>, <span class="number">10</span>, by = <span class="number">.2</span>)</span><br><span class="line"> </span><br><span class="line"><span class="comment"># Choose the mean as 2.5 and standard deviation as 2. </span></span><br><span class="line">y &lt;- pnorm(x, mean = <span class="number">2.5</span>, sd = <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;pnorm.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the graph.</span></span><br><span class="line">plot(x, y)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/pnorm.png" alt="pnorm"></p><h3 id="qnorm"><a href="#qnorm" class="headerlink" title="qnorm()"></a>qnorm()</h3><p>该函数采用概率值，并给出累积值与概率值匹配的数字。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a sequence of probability values incrementing by 0.02.</span></span><br><span class="line">x &lt;- seq(<span class="number">0</span>, <span class="number">1</span>, by = <span class="number">0.02</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Choose the mean as 2 and standard deviation as 3.</span></span><br><span class="line">y &lt;- qnorm(x, mean = <span class="number">2</span>, sd = <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;qnorm.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the graph.</span></span><br><span class="line">plot(x, y)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/qnorm.png" alt="qnorm"></p><h3 id="rnorm"><a href="#rnorm" class="headerlink" title="rnorm()"></a>rnorm()</h3><p>此函数用于生成分布正常的随机数。它将样本大小作为输入，并生成许多随机数。我们绘制一个直方图来显示生成的数字的分布。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a sample of 50 numbers which are normally distributed.</span></span><br><span class="line">y &lt;- rnorm(<span class="number">50</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;rnorm.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the histogram for this sample.</span></span><br><span class="line">hist(y, main = <span class="string">&quot;Normal DIstribution&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/rnorm.png" alt="rnorm"></p><h2 id="二项分布"><a href="#二项分布" class="headerlink" title="二项分布"></a>二项分布</h2><p>二项分布模型处理在一系列实验中仅发现两个可能结果的事件的成功概率。例如，掷硬币总是给出正或反。在二项分布期间估计在 <code>10</code> 次重复抛掷硬币中精确找到 <code>3</code> 个正的概率。</p><p>R 语言有四个内置函数来生成二项分布。它们描述如下：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">dbinom(x, size, prob)</span><br><span class="line">pbinom(x, size, prob)</span><br><span class="line">qbinom(p, size, prob)</span><br><span class="line">rbinom(n, size, prob)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是数字的向量。</li><li><code>P</code> 是概率向量。</li><li><code>n</code> 是观察的数量。</li><li><code>size</code> 是试验的数量。</li><li><code>prob</code> 是每个试验成功的概率。</li></ul><h3 id="dbinom"><a href="#dbinom" class="headerlink" title="dbinom()"></a>dbinom()</h3><p>该函数给出每个点的概率密度分布。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a sample of 50 numbers which are incremented by 1.</span></span><br><span class="line">x &lt;- seq(<span class="number">0</span>, <span class="number">50</span>, by = <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the binomial distribution.</span></span><br><span class="line">y &lt;- dbinom(x, <span class="number">50</span>, <span class="number">0.5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;dbinom.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the graph for this sample.</span></span><br><span class="line">plot(x, y)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/dbinom.png" alt="dbinom"></p><h3 id="pbinom"><a href="#pbinom" class="headerlink" title="pbinom()"></a>pbinom()</h3><p>此函数给出事件的累积概率。它是表示概率的单个值。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Probability of getting 26 or less heads from a 51 tosses of a coin.</span></span><br><span class="line">x &lt;- pbinom(<span class="number">26</span>, <span class="number">51</span>, <span class="number">0.5</span>)</span><br><span class="line">print(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">0.610116</span></span><br></pre></td></tr></table></figure></p><h3 id="qbinom"><a href="#qbinom" class="headerlink" title="qbinom()"></a>qbinom()</h3><p>该函数采用概率值，并给出累积值与概率值匹配的数字。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># How many heads will have a probability of 0.25 will come out when a coin is tossed 51 times.</span></span><br><span class="line">x &lt;- qbinom(<span class="number">0.25</span>, <span class="number">51</span>, <span class="number">1</span>/<span class="number">2</span>)</span><br><span class="line">print(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">23</span></span><br></pre></td></tr></table></figure></p><h3 id="rbinom"><a href="#rbinom" class="headerlink" title="rbinom()"></a>rbinom()</h3><p>该函数从给定样本产生给定概率的所需数量的随机值。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Find 8 random values from a sample of 150 with probability of 0.4.</span></span><br><span class="line">x &lt;- rbinom(<span class="number">8</span>, <span class="number">150</span>, <span class="number">.4</span>)</span><br><span class="line">print(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">63</span> <span class="number">62</span> <span class="number">64</span> <span class="number">54</span> <span class="number">54</span> <span class="number">47</span> <span class="number">57</span> <span class="number">59</span></span><br></pre></td></tr></table></figure></p><h2 id="泊松回归"><a href="#泊松回归" class="headerlink" title="泊松回归"></a>泊松回归</h2><p>泊松回归（Poisson regression）是用来为计数资料和列联表建模的一种回归分析，其中响应变量是计数而不是分数的形式。</p><p>例如，在一个足球系列赛中出线或获胜的次数。此外，响应变量的值遵循泊松分布。</p><p>泊松回归的一般数学方程为：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">log</span>(y) = a + b1x1 + b2x2 + bnxn ...</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code>​ 是预测变量。</li><li><code>y</code> ​是响应变量。</li><li>​<code>a</code> ​和 ​<code>b</code> ​是数字系数。</li></ul><p>用于创建泊松回归模型的函数是​ <code>glm()​</code> 函数。</p><p><strong>语法</strong></p><p>在泊松回归中 ​<code>glm()</code>​ 函数的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">glm(formula, data, family)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>formula</code> ​是表示变量之间的关系的符号。</li><li><code>​data​</code> 是给出这些变量的值的数据集。</li><li><code>​family​</code> 是 R 语言对象来指定模型的细节。它的值是“泊松”的逻辑回归。</li></ul><p><strong>实例</strong></p><p>我们有内置的数据集 <code>​warpbreaks</code>，其描述了羊毛类型 <code>wool</code>（​A​ 或 ​B​）和张力 <code>tension</code>（低，中或高）对每个织机的经纱断裂数量 <code>breaks</code> 的影响。让我们考虑“断裂”作为响应变量，它是断裂次数的计数。羊毛“类型”和“张力”作为预测变量。</p><p><strong>输入数据</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">input &lt;- warpbreaks</span><br><span class="line">print(head(input))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  breaks wool tension</span><br><span class="line">1     <span class="number">26</span>    A       L</span><br><span class="line">2     <span class="number">30</span>    A       L</span><br><span class="line">3     <span class="number">54</span>    A       L</span><br><span class="line">4     <span class="number">25</span>    A       L</span><br><span class="line">5     <span class="number">70</span>    A       L</span><br><span class="line">6     <span class="number">52</span>    A       L</span><br></pre></td></tr></table></figure></p><p><strong>创建回归模型</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">output &lt;-glm(formula = breaks ~ wool + tension, data = warpbreaks, family = poisson)</span><br><span class="line">print(summary(output))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">Call:</span><br><span class="line">glm(formula = breaks ~ wool + tension, family = poisson, data = warpbreaks)</span><br><span class="line"></span><br><span class="line">Deviance Residuals: </span><br><span class="line">    Min       <span class="number">1</span>Q   Median       <span class="number">3</span>Q      Max  </span><br><span class="line">-<span class="number">3.6871</span>  -<span class="number">1.6503</span>  -<span class="number">0.4269</span>   <span class="number">1.1902</span>   <span class="number">4.2616</span>  </span><br><span class="line"></span><br><span class="line">Coefficients:</span><br><span class="line">            Estimate Std. Error z value Pr(&gt;|z|)    </span><br><span class="line">(Intercept)  <span class="number">3.69196</span>    <span class="number">0.04541</span>  <span class="number">81.302</span>  &lt; <span class="number">2e-16</span> ***</span><br><span class="line">woolB       -<span class="number">0.20599</span>    <span class="number">0.05157</span>  -<span class="number">3.994</span> <span class="number">6.49e-05</span> ***</span><br><span class="line">tensionM    -<span class="number">0.32132</span>    <span class="number">0.06027</span>  -<span class="number">5.332</span> <span class="number">9.73e-08</span> ***</span><br><span class="line">tensionH    -<span class="number">0.51849</span>    <span class="number">0.06396</span>  -<span class="number">8.107</span> <span class="number">5.21e-16</span> ***</span><br><span class="line">---</span><br><span class="line">Signif. codes:  <span class="number">0</span> ‘***’ <span class="number">0.001</span> ‘**’ <span class="number">0.01</span> ‘*’ <span class="number">0.05</span> ‘.’ <span class="number">0.1</span> ‘ ’ <span class="number">1</span></span><br><span class="line"></span><br><span class="line">(Dispersion parameter <span class="keyword">for</span> poisson family taken to be <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    Null deviance: <span class="number">297.37</span>  on <span class="number">53</span>  degrees of freedom</span><br><span class="line">Residual deviance: <span class="number">210.39</span>  on <span class="number">50</span>  degrees of freedom</span><br><span class="line">AIC: <span class="number">493.06</span></span><br><span class="line"></span><br><span class="line">Number of Fisher Scoring iterations: <span class="number">4</span></span><br></pre></td></tr></table></figure></p><p>在 <code>summary</code> 中，我们查找最后一列中的 <code>​p</code> ​值小于​ <code>0.05</code>​，以考虑预测变量对响应变量的影响。如图所示，具有张力类型 <code>​M</code> ​和​ <code>H</code>​ 的羊毛类型 ​<code>B</code> ​对断裂计数有影响。</p><h2 id="协方差分析"><a href="#协方差分析" class="headerlink" title="协方差分析"></a>协方差分析</h2><p>我们使用回归分析创建模型，描述变量在预测变量对响应变量的影响。有时，如果我们有一个类别变量，如 <code>Yes/No</code> 或 <code>Male/Female</code> 等。简单的回归分析为分类变量的每个值提供多个结果。在这种情况下，我们可以通过将分类变量与预测变量一起使用并比较分类变量的每个级别的回归线来研究分类变量的效果。这样的分析被称为协方差分析，也称为 ANCOVA。</p><p><strong>实例</strong></p><p>考虑在 R 语言中内置的数据集 <code>mtcars</code> 。在其中我们观察到字段 <code>am</code> 表示传输的类型（自动或手动）。它是值为 <code>0/1</code> 的分类变量。汽车的每加仑英里数 <code>mpg</code> 也可以取决于马力 <code>hp</code> 的值。</p><p>我们研究 <code>am</code> 的值对 <code>mpg</code> 和 <code>hp</code> 之间回归的影响。它是通过使用 <code>aov()</code> 函数，然后使用 <code>anova()</code> 函数来比较多个回归来完成的。</p><p><strong>输入数据</strong></p><p>从数据集 <code>mtcars</code> 创建一个包含字段 <code>mpg</code>，<code>hp</code> 和 <code>am</code> 的数据框。这里我们取 <code>mpg</code> 作为响应变量，<code>hp</code> 作为预测变量，<code>am</code> 作为分类变量。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">input &lt;- mtcars[, <span class="built_in">c</span>(<span class="string">&quot;am&quot;</span>, <span class="string">&quot;mpg&quot;</span>, <span class="string">&quot;hp&quot;</span>)]</span><br><span class="line">print(head(input))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">                  am  mpg  hp</span><br><span class="line">Mazda RX4          <span class="number">1</span> <span class="number">21.0</span> <span class="number">110</span></span><br><span class="line">Mazda RX4 Wag      <span class="number">1</span> <span class="number">21.0</span> <span class="number">110</span></span><br><span class="line">Datsun <span class="number">710</span>         <span class="number">1</span> <span class="number">22.8</span>  <span class="number">93</span></span><br><span class="line">Hornet <span class="number">4</span> Drive     <span class="number">0</span> <span class="number">21.4</span> <span class="number">110</span></span><br><span class="line">Hornet Sportabout  <span class="number">0</span> <span class="number">18.7</span> <span class="number">175</span></span><br><span class="line">Valiant            <span class="number">0</span> <span class="number">18.1</span> <span class="number">105</span></span><br></pre></td></tr></table></figure></p><p><strong>协方差分析</strong></p><p>我们创建一个回归模型，以 <code>hp</code> 作为预测变量，<code>mpg</code> 作为响应变量，考虑 <code>am</code> 和 <code>hp</code> 之间的相互作用。</p><p><strong>模型与分类变量和预测变量之间的相互作用</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Get the dataset.</span></span><br><span class="line">input &lt;- mtcars</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the regression model.</span></span><br><span class="line">result &lt;- aov(mpg ~ hp * am, data = input)</span><br><span class="line">print(summary(result))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">            Df Sum Sq Mean Sq <span class="built_in">F</span> value   Pr(&gt;<span class="built_in">F</span>)    </span><br><span class="line">hp           <span class="number">1</span>  <span class="number">678.4</span>   <span class="number">678.4</span>  <span class="number">77.391</span> <span class="number">1.50e-09</span> ***</span><br><span class="line">am           <span class="number">1</span>  <span class="number">202.2</span>   <span class="number">202.2</span>  <span class="number">23.072</span> <span class="number">4.75e-05</span> ***</span><br><span class="line">hp:am        <span class="number">1</span>    <span class="number">0.0</span>     <span class="number">0.0</span>   <span class="number">0.001</span>    <span class="number">0.981</span>    </span><br><span class="line">Residuals   <span class="number">28</span>  <span class="number">245.4</span>     <span class="number">8.8</span>                     </span><br><span class="line">---</span><br><span class="line">Signif. codes:  <span class="number">0</span> ‘***’ <span class="number">0.001</span> ‘**’ <span class="number">0.01</span> ‘*’ <span class="number">0.05</span> ‘.’ <span class="number">0.1</span> ‘ ’ <span class="number">1</span></span><br></pre></td></tr></table></figure></p><p>这个结果表明，马力 <code>hp</code> 和传输类型 <code>am</code> 对每加仑的英里 <code>mpg</code> 有显着的影响，因为两种情况下的 <code>P</code> 值都小于 <code>0.05</code>。但是这两个变量之间的相互作用不显着，因为 <code>hp:am</code> 的 <code>P</code> 值大于 <code>0.05</code>。</p><p><strong>没有分类变量和预测变量之间相互作用的模型</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Get the dataset.</span></span><br><span class="line">input &lt;- mtcars</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the regression model.</span></span><br><span class="line">result &lt;- aov(mpg ~ hp + am, data = input)</span><br><span class="line">print(summary(result))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">            Df Sum Sq Mean Sq <span class="built_in">F</span> value   Pr(&gt;<span class="built_in">F</span>)    </span><br><span class="line">hp           <span class="number">1</span>  <span class="number">678.4</span>   <span class="number">678.4</span>   <span class="number">80.15</span> <span class="number">7.63e-10</span> ***</span><br><span class="line">am           <span class="number">1</span>  <span class="number">202.2</span>   <span class="number">202.2</span>   <span class="number">23.89</span> <span class="number">3.46e-05</span> ***</span><br><span class="line">Residuals   <span class="number">29</span>  <span class="number">245.4</span>     <span class="number">8.5</span>                     </span><br><span class="line">---</span><br><span class="line">Signif. codes:  <span class="number">0</span> ‘***’ <span class="number">0.001</span> ‘**’ <span class="number">0.01</span> ‘*’ <span class="number">0.05</span> ‘.’ <span class="number">0.1</span> ‘ ’ <span class="number">1</span></span><br></pre></td></tr></table></figure></p><p>这个结果表明，马力 <code>hp</code> 和传输类型 <code>am</code> 对每加仑的英里 <code>mpg</code> 有显着的影响，因为两种情况下的 <code>P</code> 值都小于 <code>0.05</code>。</p><p><strong>比较两个模型</strong></p><p>现在我们可以比较两个模型来得出结论，变量的相互作用是否真正重要。为此，我们使用 <code>anova()</code> 函数。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Get the dataset.</span></span><br><span class="line">input &lt;- mtcars</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the regression models.</span></span><br><span class="line">result1 &lt;- aov(mpg ~ hp * am, data = input)</span><br><span class="line">result2 &lt;- aov(mpg ~ hp + am, data = input)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Compare the two models.</span></span><br><span class="line">print(anova(result1, result2))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">Analysis of Variance Table</span><br><span class="line"></span><br><span class="line">Model <span class="number">1</span>: mpg ~ hp * am</span><br><span class="line">Model <span class="number">2</span>: mpg ~ hp + am</span><br><span class="line">  Res.Df    RSS Df  Sum of Sq     <span class="built_in">F</span> Pr(&gt;<span class="built_in">F</span>)</span><br><span class="line">1     <span class="number">28</span> <span class="number">245.43</span>                           </span><br><span class="line">2     <span class="number">29</span> <span class="number">245.44</span> -<span class="number">1</span> -<span class="number">0.0052515</span> <span class="number">6e-04</span> <span class="number">0.9806</span></span><br></pre></td></tr></table></figure></p><p>由于 <code>P</code> 值大于 <code>0.05</code>，我们得出结论，马力 <code>hp</code> 和传播类型 <code>am</code> 之间的相互作用不显着。因此，在汽车和手动变速器模式下，每加仑的里程将以类似的方式取决于汽车的马力。</p><h2 id="时间序列分析"><a href="#时间序列分析" class="headerlink" title="时间序列分析"></a>时间序列分析</h2><p>时间序列是将统一统计值按照时间发生的先后顺序来进行排列，时间序列分析的主要目的是根据已有数据对未来进行预测。</p><p>一个稳定的时间序列中常常包含两个部分，那么就是：有规律的时间序列 + 噪声。所以，在以下的方法中，主要的目的就是去过滤噪声值，让我们的时间序列更加的有分析意义。</p><p><strong>语法</strong></p><p>时间序列分析中 <code>ts()</code> 函数的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">timeseries.object.name &lt;- ts(data, start, end, frequency)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>data</code> 是包含在时间序列中使用的值的向量或矩阵。</li><li><code>start</code> 以时间序列指定第一次观察的开始时间。</li><li><code>end</code> 指定时间序列中最后一次观测的结束时间。</li><li><code>frequency</code> 指定每单位时间的观测数。</li></ul><p>除了参数 <code>data</code>，所有其他参数是可选的。</p><p><strong>时间序列的预处理</strong></p><ol><li><p>平稳性检验</p><p> 拿到一个时间序列之后，我们首先要对其稳定性进行判断，只有非白噪声的稳定性时间序列才有分析的意义以及预测未来数据的价值。</p><p> 所谓平稳，是指统计值在一个常数上下波动并且波动范围是有界限的。如果有明显的趋势或者周期性，那么就是不稳定的。一般判断有三种方法：</p><ul><li>画出时间序列的趋势图，看趋势判断</li><li>画自相关图和偏相关图，平稳时间序列的自相关图和偏相关图，要么拖尾，要么截尾。</li><li><p>检验序列中是否存在单位根，如果存在单位根，就是非平稳时间序列。</p><p>在 R 语言中，<code>DF</code> 检测是一种检测稳定性的方法，如果得出的 <code>P</code> 值小于临界值，则认为是数列是稳定的。</p></li></ul></li><li><p>白噪声检验</p><p> 白噪声序列，又称为纯随机性序列，序列的各个值之间没有任何的相关关系，序列在进行无序的随机波动，可以终止对该序列的分析，因为从白噪声序列中是提取不到任何有价值的信息的。</p></li><li><p>平稳时间序列的参数特点</p><p> 均值和方差为常数，并且具有与时间无关的自协方差。</p></li></ol><p><strong>时间序列建模步骤</strong></p><ol><li><p>拿到被分析的时间序列数据集。</p></li><li><p>对数据绘图，观测其平稳性。若为非平稳时间序列要先进行 <code>d</code> 阶差分运算后化为平稳时间序列，此处的 <code>d</code> 即为 <code>ARIMA(p,d,q)</code> 模型中的 <code>d</code>；若为平稳序列，则用 <code>ARMA(p,q)</code> 模型。所以 <code>ARIMA(p,d,q)</code> 模型区别于 <code>ARMA(p,q)</code> 之处就在于前者的自回归部分的特征多项式含有 <code>d</code> 个单位根。</p></li><li><p>对得到的平稳时间序列分别求得其自相关系数 <code>ACF</code> 和偏自相关系数 <code>PACF</code>，通过对自相关图和偏自相关图的分析，得到最佳的阶层 <code>p</code> 和阶数 <code>q</code>。由以上得到的 <code>d</code>、<code>q</code>、<code>p</code>，得到 <code>ARIMA</code> 模型。</p></li><li><p>模型诊断。进行诊断分析，以证实所得模型确实与所观察到的数据特征相符。若不相符，重新回到第 3 步。</p></li></ol><p><strong>实例</strong></p><p>考虑从 2012 年 1 月开始的一个地方的年降雨量细节。我们创建一个 R 时间序列对象为期 12 个月并绘制它。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Get the data points in form of a R vector.</span></span><br><span class="line">rainfall &lt;- <span class="built_in">c</span>(<span class="number">799</span>, <span class="number">1174.8</span>, <span class="number">865.1</span>, <span class="number">1334.6</span>, <span class="number">635.4</span>, <span class="number">918.5</span>, <span class="number">685.5</span>, <span class="number">998.6</span>, <span class="number">784.2</span>, <span class="number">985</span>, <span class="number">882.8</span>, <span class="number">1071</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Convert it to a time series object.</span></span><br><span class="line">rainfall.timeseries &lt;- ts(rainfall, start = <span class="built_in">c</span>(<span class="number">2012</span>, <span class="number">1</span>), frequency = <span class="number">12</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the timeseries data.</span></span><br><span class="line">print(rainfall.timeseries)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;rainfall.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot a graph of the time series.</span></span><br><span class="line">plot(rainfall.timeseries)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果及图表：</span></span><br><span class="line"></span><br><span class="line">        Jan    Feb    Mar    Apr    May    Jun    Jul    Aug    Sep    Oct    Nov    Dec</span><br><span class="line">2012  <span class="number">799.0</span> <span class="number">1174.8</span>  <span class="number">865.1</span> <span class="number">1334.6</span>  <span class="number">635.4</span>  <span class="number">918.5</span>  <span class="number">685.5</span>  <span class="number">998.6</span>  <span class="number">784.2</span>  <span class="number">985.0</span>  <span class="number">882.8</span> <span class="number">1071.0</span></span><br></pre></td></tr></table></figure></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/rainfall.png" alt="rainfall"></p><p><strong>不同的时间间隔</strong></p><p><code>ts()</code> 函数中的频率参数值决定了测量数据点的时间间隔。值为 12 表示时间序列为 12 个月。其他值及其含义如下：</p><ul><li><code>frequency = 12</code>：指定一年中每个月的数据点。</li><li><code>frequency = 4</code>：每年的每个季度的数据点。</li><li><code>frequency = 6</code>：每小时的 10 分钟的数据点。</li><li><code>frequency = 24 * 6</code>：将一天的每 10 分钟的数据点固定。</li></ul><p><strong>多时间序列</strong></p><p>我们可以通过将两个系列组合成一个矩阵，在一个图表中绘制多个时间序列。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Get the data points in form of a R vector.</span></span><br><span class="line">rainfall1 &lt;- <span class="built_in">c</span>(<span class="number">799</span>, <span class="number">1174.8</span>, <span class="number">865.1</span>, <span class="number">1334.6</span>, <span class="number">635.4</span>, <span class="number">918.5</span>, <span class="number">685.5</span>, <span class="number">998.6</span>, <span class="number">784.2</span>, <span class="number">985</span>, <span class="number">882.8</span>, <span class="number">1071</span>)</span><br><span class="line">rainfall2 &lt;- <span class="built_in">c</span>(<span class="number">655</span>, <span class="number">1306.9</span>, <span class="number">1323.4</span>, <span class="number">1172.2</span>, <span class="number">562.2</span>, <span class="number">824</span>, <span class="number">822.4</span>, <span class="number">1265.5</span>, <span class="number">799.6</span>, <span class="number">1105.6</span>, <span class="number">1106.7</span>, <span class="number">1337.8</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Convert them to a matrix.</span></span><br><span class="line">combined.rainfall &lt;- matrix(<span class="built_in">c</span>(rainfall1, rainfall2), nrow = <span class="number">12</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Convert it to a time series object.</span></span><br><span class="line">rainfall.timeseries &lt;- ts(combined.rainfall, start = <span class="built_in">c</span>(<span class="number">2012</span>, <span class="number">1</span>), frequency = <span class="number">12</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the timeseries data.</span></span><br><span class="line">print(rainfall.timeseries)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;rainfall_combined.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot a graph of the time series.</span></span><br><span class="line">plot(rainfall.timeseries, main = <span class="string">&quot;Multiple Time Series&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果及图表：</span></span><br><span class="line"></span><br><span class="line">         Series <span class="number">1</span> Series <span class="number">2</span></span><br><span class="line">Jan <span class="number">2012</span>    <span class="number">799.0</span>    <span class="number">655.0</span></span><br><span class="line">Feb <span class="number">2012</span>   <span class="number">1174.8</span>   <span class="number">1306.9</span></span><br><span class="line">Mar <span class="number">2012</span>    <span class="number">865.1</span>   <span class="number">1323.4</span></span><br><span class="line">Apr <span class="number">2012</span>   <span class="number">1334.6</span>   <span class="number">1172.2</span></span><br><span class="line">May <span class="number">2012</span>    <span class="number">635.4</span>    <span class="number">562.2</span></span><br><span class="line">Jun <span class="number">2012</span>    <span class="number">918.5</span>    <span class="number">824.0</span></span><br><span class="line">Jul <span class="number">2012</span>    <span class="number">685.5</span>    <span class="number">822.4</span></span><br><span class="line">Aug <span class="number">2012</span>    <span class="number">998.6</span>   <span class="number">1265.5</span></span><br><span class="line">Sep <span class="number">2012</span>    <span class="number">784.2</span>    <span class="number">799.6</span></span><br><span class="line">Oct <span class="number">2012</span>    <span class="number">985.0</span>   <span class="number">1105.6</span></span><br><span class="line">Nov <span class="number">2012</span>    <span class="number">882.8</span>   <span class="number">1106.7</span></span><br><span class="line">Dec <span class="number">2012</span>   <span class="number">1071.0</span>   <span class="number">1337.8</span></span><br></pre></td></tr></table></figure></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/rainfall_combined.png" alt="rainfall_combined"></p><h2 id="非线性最小二乘"><a href="#非线性最小二乘" class="headerlink" title="非线性最小二乘"></a>非线性最小二乘</h2><p>当模拟真实世界数据用于回归分析时，我们观察到，很少情况下，模型的方程是给出线性图的线性方程。大多数时候，真实世界数据模型的方程涉及更高程度的数学函数，如 <code>3</code> 的指数或 <code>sin</code> 函数。在这种情况下，模型的图给出了曲线而不是线。线性和非线性回归的目的是调整模型参数的值，以找到最接近您的数据的线或曲线。在找到这些值时，我们将能够以良好的精确度估计响应变量。</p><p>在最小二乘回归中，我们建立了一个回归模型，其中来自回归曲线的不同点的垂直距离的平方和被最小化。我们通常从定义的模型开始，并假设系数的一些值。然后我们应用 R 语言的 <code>nls()</code> 函数获得更准确的值以及置信区间。</p><p><strong>语法</strong></p><p>在 R 语言中创建非线性最小二乘测试的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nls(formula, data, start)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>formula</code> 是包括变量和参数的非线性模型公式。</li><li><code>data</code> 是用于计算公式中变量的数据框。</li><li><code>start</code> 是起始估计的命名列表或命名数字向量。</li></ul><p><strong>实例</strong></p><p>我们将考虑一个假设其系数的初始值的非线性模型。接下来，我们将看到这些假设值的置信区间是什么，以便我们可以判断这些值在模型中有多好。</p><p>所以让我们考虑以下的方程 <code>a = b1*x^2 + b2</code>。</p><p>让我们假设初始系数为 <code>b1 = 1</code> 和 <code>b2 = 3</code>，并将这些值拟合到 <code>nls()</code> 函数中。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">1.6</span>, <span class="number">2.1</span>, <span class="number">2</span>, <span class="number">2.23</span>, <span class="number">3.71</span>, <span class="number">3.25</span>, <span class="number">3.4</span>, <span class="number">3.86</span>, <span class="number">1.19</span>, <span class="number">2.21</span>)</span><br><span class="line">y &lt;- <span class="built_in">c</span>(<span class="number">5.19</span>, <span class="number">7.43</span>, <span class="number">6.94</span>, <span class="number">8.11</span>, <span class="number">18.75</span>, <span class="number">14.88</span>, <span class="number">16.06</span>, <span class="number">19.12</span>, <span class="number">3.21</span>, <span class="number">7.58</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;nls.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot these values.</span></span><br><span class="line">plot(x, y)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Take the assumed values and fit into the model.</span></span><br><span class="line">model &lt;- nls(y ~ b1*x^<span class="number">2</span> + b2, start = <span class="built_in">list</span>(b1 = <span class="number">1</span>, b2 = <span class="number">3</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the chart with new data by fitting it to a prediction from 100 data points.</span></span><br><span class="line">new.data &lt;- data.frame(x = seq(<span class="built_in">min</span>(x), <span class="built_in">max</span>(x), len = <span class="number">100</span>))</span><br><span class="line">lines(new.data$x, predict(model, newdata = new.data))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the sum of the squared residuals.</span></span><br><span class="line">print(<span class="built_in">sum</span>(resid(model)^<span class="number">2</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the confidence intervals on the chosen values of the coefficients.</span></span><br><span class="line">print(confint(model))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果及图表：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">1.081935</span></span><br><span class="line"></span><br><span class="line">Waiting <span class="keyword">for</span> profiling to be done...</span><br><span class="line">       <span class="number">2.5</span>%    97.5%</span><br><span class="line">b1 <span class="number">1.137708</span> <span class="number">1.253135</span></span><br><span class="line">b2 <span class="number">1.497364</span> <span class="number">2.496484</span></span><br></pre></td></tr></table></figure></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/nls.png" alt="非线性最小二乘"></p><p>我们可以得出结论，<code>b1</code> 的值更接近 <code>1</code>，而 <code>b2</code> 的值更接近 <code>2</code> 而不是 <code>3</code>。</p><h2 id="决策树"><a href="#决策树" class="headerlink" title="决策树"></a>决策树</h2><p>决策树是以树的形式表示选择及其结果的图。图中的节点表示事件或选择，并且图的边缘表示决策规则或条件。它主要用于使用 R 的机器学习和数据挖掘应用程序。</p><p>决策树的使用的例子是：预测电子邮件是垃圾邮件或非垃圾邮件，预测肿瘤癌变，或者基于这些因素预测贷款的信用风险。通常，使用观测数据（也称为训练数据）来创建模型。然后使用一组验证数据来验证和改进模型。R 具有用于创建和可视化决策树的包。对于新的预测变量集合，我们使用此模型来确定 R 包 <code>party</code> 用于创建决策树。</p><p><strong>安装 R 语言包</strong></p><p>在 R 语言控制台中使用以下命令安装软件包。您还必须安装相关软件包（如果有）。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">install.packages(<span class="string">&quot;party&quot;</span>)</span><br></pre></td></tr></table></figure></p><p><code>party</code> 包具有用于创建和分析决策树的函数 <code>ctree()</code>。</p><p><strong>语法</strong></p><p>在 R 中创建决策树的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ctree(formula, data)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>formula</code> 是描述预测变量和响应变量的公式。</li><li><code>data</code> 是所使用的数据集的名称。</li></ul><p><strong>输入数据</strong></p><p>我们将使用名为 <code>readingSkills</code> 的 R 内置数据集来创建决策树。它描述了某人的 <code>readingSkills</code> 的分数，如果我们知道变量 <code>年龄</code>， <code>shoesize</code>，<code>分数</code>，以及<code>该人是否为母语者</code>。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the party package. It will automatically load other dependent packages.</span></span><br><span class="line">library(party)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print some records from data set readingSkills.</span></span><br><span class="line">print(head(readingSkills))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  nativeSpeaker age shoeSize    score</span><br><span class="line">1           yes   <span class="number">5</span> <span class="number">24.83189</span> <span class="number">32.29385</span></span><br><span class="line">2           yes   <span class="number">6</span> <span class="number">25.95238</span> <span class="number">36.63105</span></span><br><span class="line">3            no  <span class="number">11</span> <span class="number">30.42170</span> <span class="number">49.60593</span></span><br><span class="line">4           yes   <span class="number">7</span> <span class="number">28.66450</span> <span class="number">40.28456</span></span><br><span class="line">5           yes  <span class="number">11</span> <span class="number">31.88207</span> <span class="number">55.46085</span></span><br><span class="line">6           yes  <span class="number">10</span> <span class="number">30.07843</span> <span class="number">52.83124</span></span><br></pre></td></tr></table></figure></p><p><strong>实例</strong></p><p>我们将使用 <code>ctree()</code> 函数创建决策树并查看其图形。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the party package. It will automatically load other dependent packages.</span></span><br><span class="line">library(party)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the input data frame.</span></span><br><span class="line">input.dat &lt;- readingSkills[<span class="built_in">c</span>(<span class="number">1</span>:<span class="number">105</span>), ]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;decision_tree.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the tree.</span></span><br><span class="line">output.tree &lt;- ctree(nativeSpeaker ~ age + shoeSize + score, data = input.dat)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the tree.</span></span><br><span class="line">plot(output.tree)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下图表：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/decision_tree.png" alt="决策树"></p><p><strong>结论</strong></p><p>从上面显示的决策树，我们可以得出结论，其 <code>readingSkills</code> 分数低于 <code>38.3</code> 和年龄超过 <code>6</code> 的人不是一个母语者。</p><h2 id="随机森林"><a href="#随机森林" class="headerlink" title="随机森林"></a>随机森林</h2><p>在随机森林方法中，创建大量的决策树。每个观察被馈入每个决策树。每个观察的最常见的结果被用作最终输出。新的观察结果被馈入所有的树并且对每个分类模型取多数投票。</p><p>对构建树时未使用的情况进行错误估计。这称为 <code>OOB（袋外）</code> 误差估计，其被提及为百分比。</p><p>R 语言包 <code>randomForest</code> 用于创建随机森林。</p><p><strong>安装 R 包</strong></p><p>在 R 语言控制台中使用以下命令安装软件包。您还必须安装相关软件包（如果有）。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">install.packages(<span class="string">&quot;randomForest&quot;</span>)</span><br></pre></td></tr></table></figure></p><p>包 <code>randomForest</code> 具有函数 <code>randomForest()</code>，用于创建和分析随机森林。</p><p><strong>语法</strong></p><p>在 R 语言中创建随机森林的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">randomForest(formula, data)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>formula</code> 是描述预测变量和响应变量的公式。</li><li><code>data</code> 是所使用的数据集的名称。</li></ul><p><strong>输入数据</strong></p><p>我们将使用名为 <code>readingSkills</code> 的 R 语言内置数据集来创建决策树。它描述了某人的 <code>readingSkills</code> 的分数，如果我们知道变量 <code>age</code>，<code>shoesize</code>，<code>score</code>，以及<code>该人是否是母语者</code>。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the party package. It will automatically load other required packages.</span></span><br><span class="line">library(party)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print some records from data set readingSkills.</span></span><br><span class="line">print(head(readingSkills))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  nativeSpeaker age shoeSize    score</span><br><span class="line">1           yes   <span class="number">5</span> <span class="number">24.83189</span> <span class="number">32.29385</span></span><br><span class="line">2           yes   <span class="number">6</span> <span class="number">25.95238</span> <span class="number">36.63105</span></span><br><span class="line">3            no  <span class="number">11</span> <span class="number">30.42170</span> <span class="number">49.60593</span></span><br><span class="line">4           yes   <span class="number">7</span> <span class="number">28.66450</span> <span class="number">40.28456</span></span><br><span class="line">5           yes  <span class="number">11</span> <span class="number">31.88207</span> <span class="number">55.46085</span></span><br><span class="line">6           yes  <span class="number">10</span> <span class="number">30.07843</span> <span class="number">52.83124</span></span><br></pre></td></tr></table></figure></p><p><strong>实例</strong></p><p>我们将使用 <code>randomForest()</code> 函数来创建决策树并查看它的图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the party package. It will automatically load other required packages.</span></span><br><span class="line">library(party)</span><br><span class="line">library(randomForest)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the forest.</span></span><br><span class="line">output.forest &lt;- randomForest(nativeSpeaker ~ age + shoeSize + score, data = readingSkills)</span><br><span class="line"></span><br><span class="line"><span class="comment"># View the forest results.</span></span><br><span class="line">print(output.forest)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Importance of each predictor.</span></span><br><span class="line">print(importance(fit, type = <span class="number">2</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">Call:</span><br><span class="line"> randomForest(formula = nativeSpeaker ~ age + shoeSize + score, data = readingSkills) </span><br><span class="line">               Type of random forest: classification</span><br><span class="line">                     Number of trees: <span class="number">500</span></span><br><span class="line">No. of variables tried at each split: <span class="number">1</span></span><br><span class="line"></span><br><span class="line">        OOB estimate of  error rate: <span class="number">1.5</span>%</span><br><span class="line">Confusion matrix:</span><br><span class="line">    no yes class.error</span><br><span class="line">no  99   1        0.01</span><br><span class="line">yes  2  98        0.02</span><br><span class="line"></span><br><span class="line">         MeanDecreaseGini</span><br><span class="line">age              13.95406</span><br><span class="line">shoeSize         18.91006</span><br><span class="line">score            56.73051</span><br></pre></td></tr></table></figure></p><p>结论从上面显示的随机森林，我们可以得出结论，鞋码 <code>shoeSize</code> 和成绩 <code>score</code> 是决定如果某人是母语者或不是母语的重要因素。此外，该模型只有 <code>1%</code> 的误差，这意味着我们可以预测精度为 <code>99%</code>。</p><h2 id="生存分析"><a href="#生存分析" class="headerlink" title="生存分析"></a>生存分析</h2><p>生存分析处理预测特定事件将要发生的时间。它也被称为故障时间分析或分析死亡时间。例如，预测患有癌症的人将存活的天数或预测机械系统将失败的时间。</p><p>命名为 <code>survival</code> 的 R 语言包用于进行生存分析。此包包含函数 <code>Surv()</code>，它将输入数据作为 R 语言公式，并在选择的变量中创建一个生存对象用于分析。然后我们使用函数 <code>survfit()</code> 创建一个分析图。</p><p><strong>安装软件包</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">install.packages(<span class="string">&quot;survival&quot;</span>)</span><br></pre></td></tr></table></figure></p><p><strong>语法</strong></p><p>在 R 语言中创建生存分析的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Surv(time, event)</span><br><span class="line">survfit(formula)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>time</code> 是直到事件发生的跟踪时间。</li><li><code>event</code> 指示预期事件的发生的状态。</li><li><code>formula</code> 是预测变量之间的关系。</li></ul><p><strong>实例</strong></p><p>我们将考虑在上面安装的 <code>survival</code> 包中存在的名为 <code>pbc</code> 的数据集。它描述了关于受肝原发性胆汁性肝硬化 <code>PBC</code> 影响的人的生存数据点。在数据集中存在的许多列中，我们主要关注字段 <code>time</code> 和 <code>status</code>。时间表示在接受肝移植或患者死亡的患者的登记和事件的较早之间的天数。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the library.</span></span><br><span class="line">library(<span class="string">&quot;survival&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print first few rows.</span></span><br><span class="line">print(head(pbc))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id time status trt      age sex ascites hepato spiders edema bili chol albumin copper alk.phos    ast trig platelet protime stage</span><br><span class="line">1  <span class="number">1</span>  <span class="number">400</span>      <span class="number">2</span>   <span class="number">1</span> <span class="number">58.76523</span>   f       <span class="number">1</span>      <span class="number">1</span>       <span class="number">1</span>   <span class="number">1.0</span> <span class="number">14.5</span>  <span class="number">261</span>    <span class="number">2.60</span>    <span class="number">156</span>   <span class="number">1718.0</span> <span class="number">137.95</span>  <span class="number">172</span>      <span class="number">190</span>    <span class="number">12.2</span>     <span class="number">4</span></span><br><span class="line">2  <span class="number">2</span> <span class="number">4500</span>      <span class="number">0</span>   <span class="number">1</span> <span class="number">56.44627</span>   f       <span class="number">0</span>      <span class="number">1</span>       <span class="number">1</span>   <span class="number">0.0</span>  <span class="number">1.1</span>  <span class="number">302</span>    <span class="number">4.14</span>     <span class="number">54</span>   <span class="number">7394.8</span> <span class="number">113.52</span>   <span class="number">88</span>      <span class="number">221</span>    <span class="number">10.6</span>     <span class="number">3</span></span><br><span class="line">3  <span class="number">3</span> <span class="number">1012</span>      <span class="number">2</span>   <span class="number">1</span> <span class="number">70.07255</span>   m       <span class="number">0</span>      <span class="number">0</span>       <span class="number">0</span>   <span class="number">0.5</span>  <span class="number">1.4</span>  <span class="number">176</span>    <span class="number">3.48</span>    <span class="number">210</span>    <span class="number">516.0</span>  <span class="number">96.10</span>   <span class="number">55</span>      <span class="number">151</span>    <span class="number">12.0</span>     <span class="number">4</span></span><br><span class="line">4  <span class="number">4</span> <span class="number">1925</span>      <span class="number">2</span>   <span class="number">1</span> <span class="number">54.74059</span>   f       <span class="number">0</span>      <span class="number">1</span>       <span class="number">1</span>   <span class="number">0.5</span>  <span class="number">1.8</span>  <span class="number">244</span>    <span class="number">2.54</span>     <span class="number">64</span>   <span class="number">6121.8</span>  <span class="number">60.63</span>   <span class="number">92</span>      <span class="number">183</span>    <span class="number">10.3</span>     <span class="number">4</span></span><br><span class="line">5  <span class="number">5</span> <span class="number">1504</span>      <span class="number">1</span>   <span class="number">2</span> <span class="number">38.10541</span>   f       <span class="number">0</span>      <span class="number">1</span>       <span class="number">1</span>   <span class="number">0.0</span>  <span class="number">3.4</span>  <span class="number">279</span>    <span class="number">3.53</span>    <span class="number">143</span>    <span class="number">671.0</span> <span class="number">113.15</span>   <span class="number">72</span>      <span class="number">136</span>    <span class="number">10.9</span>     <span class="number">3</span></span><br><span class="line">6  <span class="number">6</span> <span class="number">2503</span>      <span class="number">2</span>   <span class="number">2</span> <span class="number">66.25873</span>   f       <span class="number">0</span>      <span class="number">1</span>       <span class="number">0</span>   <span class="number">0.0</span>  <span class="number">0.8</span>  <span class="number">248</span>    <span class="number">3.98</span>     <span class="number">50</span>    <span class="number">944.0</span>  <span class="number">93.00</span>   <span class="number">63</span>       <span class="literal">NA</span>    <span class="number">11.0</span>     <span class="number">3</span></span><br></pre></td></tr></table></figure></p><p>从上述数据，我们正在考虑分析的时间和状态。</p><p><strong>应用 <code>Surv()</code> 和 <code>survfit()</code> 函数</strong></p><p>现在我们继续应用 <code>Surv()</code> 函数到上面的数据集，并创建一个将显示趋势图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the library.</span></span><br><span class="line">library(<span class="string">&quot;survival&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the survival object. </span></span><br><span class="line">survfit(Surv(pbc$time, pbc$status == <span class="number">2</span>) ~ <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;survival.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the graph. </span></span><br><span class="line">plot(survfit(Surv(pbc$time, pbc$status == <span class="number">2</span>) ~ <span class="number">1</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果及图表：</span></span><br><span class="line"></span><br><span class="line">Call: survfit(formula = Surv(pbc$time, pbc$status == <span class="number">2</span>) ~ <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">      n  events  median <span class="number">0.95L</span>CL <span class="number">0.95</span>UCL </span><br><span class="line">    <span class="number">418</span>     <span class="number">161</span>    <span class="number">3395</span>    <span class="number">3090</span>    <span class="number">3853</span> </span><br></pre></td></tr></table></figure></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_statistics/survival.png" alt="生存概率"></p><p>上图中的趋势有助于我们预测在特定天数结束时的生存概率。</p><h2 id="卡方检验"><a href="#卡方检验" class="headerlink" title="卡方检验"></a>卡方检验</h2><p>卡方检验是一种确定两个分类变量之间是否存在显着相关性的统计方法。这两个变量应该来自相同的人口，他们应该是类似：<code>是/否</code>，<code>男/女</code>，<code>红/绿</code> 等。</p><p>例如，我们可以建立一个观察人们的冰淇淋购买模式的数据集，并尝试将一个人的性别与他们喜欢的冰淇淋的味道相关联。如果发现相关性，我们可以通过了解访问的人的性别的数量来计划适当的味道库存。</p><p><strong>语法</strong></p><p>用于执行卡方检验的函数是 <code>chisq.test()</code>。</p><p>在 R 语言中创建卡方检验的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chisq.test(data)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>data</code> 是以包含观察中变量的计数值的表的形式的数据。</li></ul><p><strong>实例</strong></p><p>我们将在 <code>MASS</code> 包中获取 <code>Cars93</code> 数据，代表 1993 年不同型号汽车的销售额。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">library(<span class="string">&quot;MASS&quot;</span>)</span><br><span class="line">print(str(Cars93))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;data.frame&#x27;</span>:<span class="number">93</span> obs. of  <span class="number">27</span> variables:</span><br><span class="line"> $ Manufacturer      : Factor w/ <span class="number">32</span> levels <span class="string">&quot;Acura&quot;</span>,<span class="string">&quot;Audi&quot;</span>,..: <span class="number">1</span> <span class="number">1</span> <span class="number">2</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">4</span> <span class="number">4</span> <span class="number">4</span> <span class="number">5</span> ...</span><br><span class="line"> $ Model             : Factor w/ <span class="number">93</span> levels <span class="string">&quot;100&quot;</span>,<span class="string">&quot;190E&quot;</span>,<span class="string">&quot;240&quot;</span>,..: <span class="number">49</span> <span class="number">56</span> <span class="number">9</span> <span class="number">1</span> <span class="number">6</span> <span class="number">24</span> <span class="number">54</span> <span class="number">74</span> <span class="number">73</span> <span class="number">35</span> ...</span><br><span class="line"> $ Type              : Factor w/ <span class="number">6</span> levels <span class="string">&quot;Compact&quot;</span>,<span class="string">&quot;Large&quot;</span>,..: <span class="number">4</span> <span class="number">3</span> <span class="number">1</span> <span class="number">3</span> <span class="number">3</span> <span class="number">3</span> <span class="number">2</span> <span class="number">2</span> <span class="number">3</span> <span class="number">2</span> ...</span><br><span class="line"> $ Min.Price         : num  <span class="number">12.9</span> <span class="number">29.2</span> <span class="number">25.9</span> <span class="number">30.8</span> <span class="number">23.7</span> <span class="number">14.2</span> <span class="number">19.9</span> <span class="number">22.6</span> <span class="number">26.3</span> <span class="number">33</span> ...</span><br><span class="line"> $ Price             : num  <span class="number">15.9</span> <span class="number">33.9</span> <span class="number">29.1</span> <span class="number">37.7</span> <span class="number">30</span> <span class="number">15.7</span> <span class="number">20.8</span> <span class="number">23.7</span> <span class="number">26.3</span> <span class="number">34.7</span> ...</span><br><span class="line"> $ Max.Price         : num  <span class="number">18.8</span> <span class="number">38.7</span> <span class="number">32.3</span> <span class="number">44.6</span> <span class="number">36.2</span> <span class="number">17.3</span> <span class="number">21.7</span> <span class="number">24.9</span> <span class="number">26.3</span> <span class="number">36.3</span> ...</span><br><span class="line"> $ MPG.city          : int  <span class="number">25</span> <span class="number">18</span> <span class="number">20</span> <span class="number">19</span> <span class="number">22</span> <span class="number">22</span> <span class="number">19</span> <span class="number">16</span> <span class="number">19</span> <span class="number">16</span> ...</span><br><span class="line"> $ MPG.highway       : int  <span class="number">31</span> <span class="number">25</span> <span class="number">26</span> <span class="number">26</span> <span class="number">30</span> <span class="number">31</span> <span class="number">28</span> <span class="number">25</span> <span class="number">27</span> <span class="number">25</span> ...</span><br><span class="line"> $ AirBags           : Factor w/ <span class="number">3</span> levels <span class="string">&quot;Driver &amp; Passenger&quot;</span>,..: <span class="number">3</span> <span class="number">1</span> <span class="number">2</span> <span class="number">1</span> <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> ...</span><br><span class="line"> $ DriveTrain        : Factor w/ <span class="number">3</span> levels <span class="string">&quot;4WD&quot;</span>,<span class="string">&quot;Front&quot;</span>,..: <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">3</span> <span class="number">2</span> <span class="number">2</span> <span class="number">3</span> <span class="number">2</span> <span class="number">2</span> ...</span><br><span class="line"> $ Cylinders         : Factor w/ <span class="number">6</span> levels <span class="string">&quot;3&quot;</span>,<span class="string">&quot;4&quot;</span>,<span class="string">&quot;5&quot;</span>,<span class="string">&quot;6&quot;</span>,..: <span class="number">2</span> <span class="number">4</span> <span class="number">4</span> <span class="number">4</span> <span class="number">2</span> <span class="number">2</span> <span class="number">4</span> <span class="number">4</span> <span class="number">4</span> <span class="number">5</span> ...</span><br><span class="line"> $ EngineSize        : num  <span class="number">1.8</span> <span class="number">3.2</span> <span class="number">2.8</span> <span class="number">2.8</span> <span class="number">3.5</span> <span class="number">2.2</span> <span class="number">3.8</span> <span class="number">5.7</span> <span class="number">3.8</span> <span class="number">4.9</span> ...</span><br><span class="line"> $ Horsepower        : int  <span class="number">140</span> <span class="number">200</span> <span class="number">172</span> <span class="number">172</span> <span class="number">208</span> <span class="number">110</span> <span class="number">170</span> <span class="number">180</span> <span class="number">170</span> <span class="number">200</span> ...</span><br><span class="line"> $ RPM               : int  <span class="number">6300</span> <span class="number">5500</span> <span class="number">5500</span> <span class="number">5500</span> <span class="number">5700</span> <span class="number">5200</span> <span class="number">4800</span> <span class="number">4000</span> <span class="number">4800</span> <span class="number">4100</span> ...</span><br><span class="line"> $ Rev.per.mile      : int  <span class="number">2890</span> <span class="number">2335</span> <span class="number">2280</span> <span class="number">2535</span> <span class="number">2545</span> <span class="number">2565</span> <span class="number">1570</span> <span class="number">1320</span> <span class="number">1690</span> <span class="number">1510</span> ...</span><br><span class="line"> $ Man.trans.avail   : Factor w/ <span class="number">2</span> levels <span class="string">&quot;No&quot;</span>,<span class="string">&quot;Yes&quot;</span>: <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">1</span> <span class="number">1</span> <span class="number">1</span> <span class="number">1</span> <span class="number">1</span> ...</span><br><span class="line"> $ Fuel.tank.capacity: num  <span class="number">13.2</span> <span class="number">18</span> <span class="number">16.9</span> <span class="number">21.1</span> <span class="number">21.1</span> <span class="number">16.4</span> <span class="number">18</span> <span class="number">23</span> <span class="number">18.8</span> <span class="number">18</span> ...</span><br><span class="line"> $ Passengers        : int  <span class="number">5</span> <span class="number">5</span> <span class="number">5</span> <span class="number">6</span> <span class="number">4</span> <span class="number">6</span> <span class="number">6</span> <span class="number">6</span> <span class="number">5</span> <span class="number">6</span> ...</span><br><span class="line"> $ Length            : int  <span class="number">177</span> <span class="number">195</span> <span class="number">180</span> <span class="number">193</span> <span class="number">186</span> <span class="number">189</span> <span class="number">200</span> <span class="number">216</span> <span class="number">198</span> <span class="number">206</span> ...</span><br><span class="line"> $ Wheelbase         : int  <span class="number">102</span> <span class="number">115</span> <span class="number">102</span> <span class="number">106</span> <span class="number">109</span> <span class="number">105</span> <span class="number">111</span> <span class="number">116</span> <span class="number">108</span> <span class="number">114</span> ...</span><br><span class="line"> $ Width             : int  <span class="number">68</span> <span class="number">71</span> <span class="number">67</span> <span class="number">70</span> <span class="number">69</span> <span class="number">69</span> <span class="number">74</span> <span class="number">78</span> <span class="number">73</span> <span class="number">73</span> ...</span><br><span class="line"> $ Turn.circle       : int  <span class="number">37</span> <span class="number">38</span> <span class="number">37</span> <span class="number">37</span> <span class="number">39</span> <span class="number">41</span> <span class="number">42</span> <span class="number">45</span> <span class="number">41</span> <span class="number">43</span> ...</span><br><span class="line"> $ Rear.seat.room    : num  <span class="number">26.5</span> <span class="number">30</span> <span class="number">28</span> <span class="number">31</span> <span class="number">27</span> <span class="number">28</span> <span class="number">30.5</span> <span class="number">30.5</span> <span class="number">26.5</span> <span class="number">35</span> ...</span><br><span class="line"> $ Luggage.room      : int  <span class="number">11</span> <span class="number">15</span> <span class="number">14</span> <span class="number">17</span> <span class="number">13</span> <span class="number">16</span> <span class="number">17</span> <span class="number">21</span> <span class="number">14</span> <span class="number">18</span> ...</span><br><span class="line"> $ Weight            : int  <span class="number">2705</span> <span class="number">3560</span> <span class="number">3375</span> <span class="number">3405</span> <span class="number">3640</span> <span class="number">2880</span> <span class="number">3470</span> <span class="number">4105</span> <span class="number">3495</span> <span class="number">3620</span> ...</span><br><span class="line"> $ Origin            : Factor w/ <span class="number">2</span> levels <span class="string">&quot;USA&quot;</span>,<span class="string">&quot;non-USA&quot;</span>: <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">2</span> <span class="number">1</span> <span class="number">1</span> <span class="number">1</span> <span class="number">1</span> <span class="number">1</span> ...</span><br><span class="line"> $ Make              : Factor w/ <span class="number">93</span> levels <span class="string">&quot;Acura Integra&quot;</span>,..: <span class="number">1</span> <span class="number">2</span> <span class="number">4</span> <span class="number">3</span> <span class="number">5</span> <span class="number">6</span> <span class="number">7</span> <span class="number">9</span> <span class="number">8</span> <span class="number">10</span> ...</span><br></pre></td></tr></table></figure></p><p>上述结果表明数据集有很多因素变量，可以被认为是分类变量。对于我们的模型，我们将考虑变量 <code>AirBags</code> 和 <code>Type</code>。在这里，我们的目标是找出所售的汽车类型和安全气囊类型之间的任何显着的相关性。如果观察到相关性，我们可以估计哪种类型的汽车可以更好地卖什么类型的气囊。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the library.</span></span><br><span class="line">library(<span class="string">&quot;MASS&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create a data frame from the main data set.</span></span><br><span class="line">car.data &lt;- data.frame(Cars93$AirBags, Cars93$Type)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create a table with the needed variables.</span></span><br><span class="line">car.data = table(Cars93$AirBags, Cars93$Type) </span><br><span class="line">print(car.data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Perform the Chi-Square test.</span></span><br><span class="line">print(chisq.test(car.data))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">                     Compact Large Midsize Small Sporty Van</span><br><span class="line">  Driver &amp; Passenger       <span class="number">2</span>     <span class="number">4</span>       <span class="number">7</span>     <span class="number">0</span>      <span class="number">3</span>   <span class="number">0</span></span><br><span class="line">  Driver only              <span class="number">9</span>     <span class="number">7</span>      <span class="number">11</span>     <span class="number">5</span>      <span class="number">8</span>   <span class="number">3</span></span><br><span class="line">  None                     <span class="number">5</span>     <span class="number">0</span>       <span class="number">4</span>    <span class="number">16</span>      <span class="number">3</span>   <span class="number">6</span></span><br><span class="line"></span><br><span class="line">Pearson`s Chi-squared test</span><br><span class="line"></span><br><span class="line">data:  car.data</span><br><span class="line">X-squared = 33.001, df = 10, p-value = 0.0002723</span><br><span class="line"></span><br><span class="line">Warning message:</span><br><span class="line">In chisq.test(car.data) : Chi-squared approximation may be incorrect</span><br></pre></td></tr></table></figure></p><p><strong>结论</strong>结果显示 <code>P</code> 值小于 <code>0.05</code>，这表明汽车类型和安全气囊类型之间具有相关性。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/R/">R</category>
      
      
      <comments>https://blog.eurkon.com/post/741c153c.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>R 语言数据接口</title>
      <link>https://blog.eurkon.com/post/996a1090.html</link>
      <guid>https://blog.eurkon.com/post/996a1090.html</guid>
      <pubDate>Thu, 21 Jan 2021 05:52:32 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;R 语言处理的数据一般从外部导入，因此需要数据接口来读取各种格式化的数据。&lt;/p&gt;
&lt;p&gt;在 R 语言中，我们可以从存储在 R 语言环境外的</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>R 语言处理的数据一般从外部导入，因此需要数据接口来读取各种格式化的数据。</p><p>在 R 语言中，我们可以从存储在 R 语言环境外的文件中读取数据。我们还可以将数据写入将被操作系统存储和访问的文件。R 语言可以读取和写入各种文件格式，如​ <code>CSV</code>​，​<code>Excel</code>​，<code>XML</code>，<code>JSON</code> ​等。</p><h2 id="CSV-文件"><a href="#CSV-文件" class="headerlink" title="CSV 文件"></a>CSV 文件</h2><p>本章节学习从​ <code>CSV</code> 文件读取数据，然后将数据写入 <code>CSV</code> ​文件。该文件应该存在于当前工作目录中，以便 R 语言可以读取它。当然我们也可以设置我们自己的目录并从那里读取文件。</p><h3 id="获取和设置工作目录"><a href="#获取和设置工作目录" class="headerlink" title="获取和设置工作目录"></a>获取和设置工作目录</h3><p>您可以使用 ​<code>getwd()</code>​ 函数检查 R 语言工作区指向的目录。您还可以使用 <code>setwd()​</code> 函数设置新的工作目录。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Get and print current working directory.</span></span><br><span class="line">print(getwd())</span><br><span class="line"></span><br><span class="line"><span class="comment"># Set current working directory.</span></span><br><span class="line">setwd(<span class="string">&quot;E:/data&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get and print current working directory.</span></span><br><span class="line">print(getwd())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;C:/Users/user/Documents&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;E:/data&quot;</span></span><br></pre></td></tr></table></figure></p><p>此结果取决于您的操作系统和您当前工作的目录。</p><h3 id="创建-CSV-文件"><a href="#创建-CSV-文件" class="headerlink" title="创建 CSV 文件"></a>创建 CSV 文件</h3><p><code>CSV</code> 文件是一个文本文件，其中列中的值由逗号分隔。</p><p>通过将以下数据复制到文本编辑器（如记事本）中来创建文件。使用 <code>.csv</code> 扩展名保存使用记事本中的保存为所有文件​（<code>*.*</code>）​选项，将文件保存为 <code>input.csv</code>。</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">id,name,salary,start<span class="emphasis">_date,dept</span></span><br><span class="line"><span class="emphasis">1,Rick,623.3,2012-01-01,IT</span></span><br><span class="line"><span class="emphasis">2,Dan,515.2,2013-09-23,Operations</span></span><br><span class="line"><span class="emphasis">3,Michelle,611,2014-11-15,IT</span></span><br><span class="line"><span class="emphasis">4,Ryan,729,2014-05-11,HR</span></span><br><span class="line"><span class="emphasis"> ,Gary,843.25,2015-03-27,Finance</span></span><br><span class="line"><span class="emphasis">6,Nina,578,2013-05-21,IT</span></span><br><span class="line"><span class="emphasis">7,Simon,632.8,2013-07-30,Operations</span></span><br><span class="line"><span class="emphasis">8,Guru,722.5,2014-06-17,Finance</span></span><br></pre></td></tr></table></figure></p><h3 id="读取-CSV-文件"><a href="#读取-CSV-文件" class="headerlink" title="读取 CSV 文件"></a>读取 CSV 文件</h3><p>以下是​ <code>read.csv()</code> ​函数的一个简单示例，用于读取当前工作目录中可用的 <code>CSV</code> 文件。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">data &lt;- read.csv(<span class="string">&quot;input.csv&quot;</span>)</span><br><span class="line">print(data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id     name salary start_date       dept</span><br><span class="line">1  <span class="number">1</span>     Rick <span class="number">623.30</span> <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span>         IT</span><br><span class="line">2  <span class="number">2</span>      Dan <span class="number">515.20</span> <span class="number">2013</span>-<span class="number">09</span>-<span class="number">23</span> Operations</span><br><span class="line">3  <span class="number">3</span> Michelle <span class="number">611.00</span> <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>         IT</span><br><span class="line">4  <span class="number">4</span>     Ryan <span class="number">729.00</span> <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>         HR</span><br><span class="line">5 <span class="literal">NA</span>     Gary <span class="number">843.25</span> <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span>    Finance</span><br><span class="line">6  <span class="number">6</span>     Nina <span class="number">578.00</span> <span class="number">2013</span>-<span class="number">05</span>-<span class="number">21</span>         IT</span><br><span class="line">7  <span class="number">7</span>    Simon <span class="number">632.80</span> <span class="number">2013</span>-<span class="number">07</span>-<span class="number">30</span> Operations</span><br><span class="line">8  <span class="number">8</span>     Guru <span class="number">722.50</span> <span class="number">2014</span>-<span class="number">06</span>-<span class="number">17</span>    Finance</span><br></pre></td></tr></table></figure></p><h3 id="分析-CSV-文件"><a href="#分析-CSV-文件" class="headerlink" title="分析 CSV 文件"></a>分析 CSV 文件</h3><p>默认情况下，<code>​read.csv()​</code> 函数将输出作为数据框。这可以容易地如下检查。此外，我们可以检查列和行的数量。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">data &lt;- read.csv(<span class="string">&quot;input.csv&quot;</span>)</span><br><span class="line"></span><br><span class="line">print(is.data.frame(data))</span><br><span class="line">print(ncol(data))</span><br><span class="line">print(nrow(data))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="literal">TRUE</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">5</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">8</span></span><br></pre></td></tr></table></figure></p><p>一旦我们读取数据框中的数据，我们可以应用所有适用于数据框的函数。</p><p><strong>获取最高工资</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a data frame.</span></span><br><span class="line">data &lt;- read.csv(<span class="string">&quot;input.csv&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the max salary from data frame.</span></span><br><span class="line">sal &lt;- <span class="built_in">max</span>(data$salary)</span><br><span class="line">print(sal)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">843.25</span></span><br></pre></td></tr></table></figure></p><p><strong>获取具有最高工资的人的详细信息</strong></p><p>我们可以获取满足特定过滤条件的行，类似于 <code>​SQL WHERE</code> 子句。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a data frame.</span></span><br><span class="line">data &lt;- read.csv(<span class="string">&quot;input.csv&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the person detail having max salary.</span></span><br><span class="line">retval &lt;- subset(data, salary == <span class="built_in">max</span>(salary))</span><br><span class="line">print(retval)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id name salary start_date    dept</span><br><span class="line">5 <span class="literal">NA</span> Gary <span class="number">843.25</span> <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span> Finance</span><br></pre></td></tr></table></figure></p><p><strong>获取所有的 IT 部门员工的信息</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a data frame.</span></span><br><span class="line">data &lt;- read.csv(<span class="string">&quot;input.csv&quot;</span>)</span><br><span class="line"></span><br><span class="line">retval &lt;- subset( data, dept == <span class="string">&quot;IT&quot;</span>)</span><br><span class="line">print(retval)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id     name salary start_date dept</span><br><span class="line">1  <span class="number">1</span>     Rick  <span class="number">623.3</span> <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span>   IT</span><br><span class="line">3  <span class="number">3</span> Michelle  <span class="number">611.0</span> <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>   IT</span><br><span class="line">6  <span class="number">6</span>     Nina  <span class="number">578.0</span> <span class="number">2013</span>-<span class="number">05</span>-<span class="number">21</span>   IT</span><br></pre></td></tr></table></figure></p><p><strong>获取工资大于 600 的 IT 部门的人员</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a data frame.</span></span><br><span class="line">data &lt;- read.csv(<span class="string">&quot;input.csv&quot;</span>)</span><br><span class="line"></span><br><span class="line">info &lt;- subset(data, salary &gt; <span class="number">600</span> &amp; dept == <span class="string">&quot;IT&quot;</span>)</span><br><span class="line">print(info)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id     name salary start_date dept</span><br><span class="line">1  <span class="number">1</span>     Rick  <span class="number">623.3</span> <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span>   IT</span><br><span class="line">3  <span class="number">3</span> Michelle  <span class="number">611.0</span> <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>   IT</span><br></pre></td></tr></table></figure></p><p><strong>获取 2014 年或之后加入的人</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a data frame.</span></span><br><span class="line">data &lt;- read.csv(<span class="string">&quot;input.csv&quot;</span>)</span><br><span class="line"></span><br><span class="line">retval &lt;- subset(data, as.Date(start_date) &gt; as.Date(<span class="string">&quot;2014-01-01&quot;</span>))</span><br><span class="line">print(retval)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id     name salary start_date    dept</span><br><span class="line">3  <span class="number">3</span> Michelle <span class="number">611.00</span> <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>      IT</span><br><span class="line">4  <span class="number">4</span>     Ryan <span class="number">729.00</span> <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>      HR</span><br><span class="line">5 <span class="literal">NA</span>     Gary <span class="number">843.25</span> <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span> Finance</span><br><span class="line">8  <span class="number">8</span>     Guru <span class="number">722.50</span> <span class="number">2014</span>-<span class="number">06</span>-<span class="number">17</span> Finance</span><br></pre></td></tr></table></figure></p><h3 id="写入-CSV-文件"><a href="#写入-CSV-文件" class="headerlink" title="写入 CSV 文件"></a>写入 CSV 文件</h3><p>R 语言可以创建​ <code>CSV</code> 文件形式的现有数据框。​<code>write.csv()</code> ​函数用于创建​ <code>CSV</code> 文件。此文件在工作目录中创建。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a data frame.</span></span><br><span class="line">data &lt;- read.csv(<span class="string">&quot;input.csv&quot;</span>)</span><br><span class="line">retval &lt;- subset(data, as.Date(start_date) &gt; as.Date(<span class="string">&quot;2014-01-01&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Write filtered data into a new file.</span></span><br><span class="line">write.csv(retval, <span class="string">&quot;output.csv&quot;</span>)</span><br><span class="line">newdata &lt;- read.csv(<span class="string">&quot;output.csv&quot;</span>)</span><br><span class="line">print(newdata)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  X id     name salary start_date    dept</span><br><span class="line">1 <span class="number">3</span>  <span class="number">3</span> Michelle <span class="number">611.00</span> <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>      IT</span><br><span class="line">2 <span class="number">4</span>  <span class="number">4</span>     Ryan <span class="number">729.00</span> <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>      HR</span><br><span class="line">3 <span class="number">5</span> <span class="literal">NA</span>     Gary <span class="number">843.25</span> <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span> Finance</span><br><span class="line">4 <span class="number">8</span>  <span class="number">8</span>     Guru <span class="number">722.50</span> <span class="number">2014</span>-<span class="number">06</span>-<span class="number">17</span> Finance</span><br></pre></td></tr></table></figure></p><p>这里列 <code>X</code> 来自数据集 ​<code>newper</code>​。这可以在写入文件时使用附加参数 <code>row.names = FALSE</code> 删除。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a data frame.</span></span><br><span class="line">data &lt;- read.csv(<span class="string">&quot;input.csv&quot;</span>)</span><br><span class="line">retval &lt;- subset(data, as.Date(start_date) &gt; as.Date(<span class="string">&quot;2014-01-01&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Write filtered data into a new file.</span></span><br><span class="line">write.csv(retval, <span class="string">&quot;output.csv&quot;</span>, row.names = <span class="literal">FALSE</span>)</span><br><span class="line">newdata &lt;- read.csv(<span class="string">&quot;output.csv&quot;</span>)</span><br><span class="line">print(newdata)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id     name salary start_date    dept</span><br><span class="line">1  <span class="number">3</span> Michelle <span class="number">611.00</span> <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>      IT</span><br><span class="line">2  <span class="number">4</span>     Ryan <span class="number">729.00</span> <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>      HR</span><br><span class="line">3 <span class="literal">NA</span>     Gary <span class="number">843.25</span> <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span> Finance</span><br><span class="line">4  <span class="number">8</span>     Guru <span class="number">722.50</span> <span class="number">2014</span>-<span class="number">06</span>-<span class="number">17</span> Finance</span><br></pre></td></tr></table></figure></p><h2 id="Excel-文件"><a href="#Excel-文件" class="headerlink" title="Excel 文件"></a>Excel 文件</h2><p>Microsoft Excel 是最广泛使用的电子表格程序，以 <code>.xls</code> 或 <code>.xlsx</code> 格式存储数据。R 语言可以直接从这些文件使用一些 <code>Excel</code> 特定的包获取数据，如<code>XLConnect</code>，<code>xlsx</code>，<code>gdata</code> 等。</p><p>下面我们将使用 <code>xlsx</code> 包。R 语言也可以使用这个包写入 <code>Excel</code> 文件。</p><h3 id="安装-xlsx-软件包"><a href="#安装-xlsx-软件包" class="headerlink" title="安装 xlsx 软件包"></a>安装 xlsx 软件包</h3><p>您可以在 R 控制台中使用以下命令来安装 <code>xlsx</code> 软件包。它可能会要求安装一些额外的软件包这个软件包依赖。按照具有所需软件包名称的同一命令安装其他软件包。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">install.packages(<span class="string">&quot;xlsx&quot;</span>)</span><br></pre></td></tr></table></figure></p><h3 id="验证并加载-xlsx-软件包"><a href="#验证并加载-xlsx-软件包" class="headerlink" title="验证并加载 xlsx 软件包"></a>验证并加载 xlsx 软件包</h3><p>使用以下命令验证并加载 <code>xlsx</code> 软件包。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Verify the package is installed.</span></span><br><span class="line"><span class="built_in">any</span>(grepl(<span class="string">&quot;xlsx&quot;</span>, installed.packages()))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Load the library into R workspace.</span></span><br><span class="line">library(<span class="string">&quot;xlsx&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="literal">TRUE</span></span><br><span class="line">Loading required package: rJava</span><br><span class="line">Loading required package: methods</span><br><span class="line">Loading required package: xlsxjars</span><br></pre></td></tr></table></figure></p><h3 id="创建-xlsx-文件"><a href="#创建-xlsx-文件" class="headerlink" title="创建 xlsx 文件"></a>创建 xlsx 文件</h3><p>打开 Microsoft Excel，将以下数据复制并粘贴到名为 <code>sheet1</code> 的工作表中。</p><div class="table-container"><table><thead><tr><th style="text-align:center">id</th><th style="text-align:center">name</th><th style="text-align:center">salary</th><th style="text-align:center">start_date</th><th style="text-align:center">dept</th></tr></thead><tbody><tr><td style="text-align:center">1</td><td style="text-align:center">Rick</td><td style="text-align:center">623.3</td><td style="text-align:center">2012-01-01</td><td style="text-align:center">IT</td></tr><tr><td style="text-align:center">2</td><td style="text-align:center">Dan</td><td style="text-align:center">515.2</td><td style="text-align:center">2013-09-23</td><td style="text-align:center">Operations</td></tr><tr><td style="text-align:center">3</td><td style="text-align:center">Michelle</td><td style="text-align:center">611</td><td style="text-align:center">2014-11-15</td><td style="text-align:center">IT</td></tr><tr><td style="text-align:center">4</td><td style="text-align:center">Ryan</td><td style="text-align:center">729</td><td style="text-align:center">2014-05-11</td><td style="text-align:center">HR</td></tr><tr><td style="text-align:center">&nbsp;</td><td style="text-align:center">Gary</td><td style="text-align:center">843.25</td><td style="text-align:center">2015-03-27</td><td style="text-align:center">Finance</td></tr><tr><td style="text-align:center">6</td><td style="text-align:center">Nina</td><td style="text-align:center">578</td><td style="text-align:center">2013-05-21</td><td style="text-align:center">IT</td></tr><tr><td style="text-align:center">7</td><td style="text-align:center">Simon</td><td style="text-align:center">632.8</td><td style="text-align:center">2013-07-30</td><td style="text-align:center">Operations</td></tr><tr><td style="text-align:center">8</td><td style="text-align:center">Guru</td><td style="text-align:center">722.5</td><td style="text-align:center">2014-06-17</td><td style="text-align:center">Finance</td></tr></tbody></table></div><p>还要将以下数据复制并粘贴到另一个工作表，并将此工作表重命名为 <code>city</code>。</p><div class="table-container"><table><thead><tr><th style="text-align:center">name</th><th style="text-align:center">city</th></tr></thead><tbody><tr><td style="text-align:center">Rick</td><td style="text-align:center">Seattle</td></tr><tr><td style="text-align:center">Dan</td><td style="text-align:center">Tampa</td></tr><tr><td style="text-align:center">Michelle</td><td style="text-align:center">Chicago</td></tr><tr><td style="text-align:center">Ryan</td><td style="text-align:center">Seattle</td></tr><tr><td style="text-align:center">Gary</td><td style="text-align:center">Houston</td></tr><tr><td style="text-align:center">Nina</td><td style="text-align:center">Boston</td></tr><tr><td style="text-align:center">Simon</td><td style="text-align:center">Mumbai</td></tr><tr><td style="text-align:center">Guru</td><td style="text-align:center">Dallas</td></tr></tbody></table></div><p>将 Excel 文件另存为 <code>input.xlsx</code>。应将其保存在 R 工作区的当前工作目录中。</p><h3 id="读取-Excel-文件"><a href="#读取-Excel-文件" class="headerlink" title="读取 Excel 文件"></a>读取 Excel 文件</h3><p>通过使用 <code>read.xlsx()</code> 函数读取 <code>input.xlsx</code>，以下脚本读取第一个工作表的数据。结果作为数据框存储在 R 语言环境中。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Read the first worksheet in the file input.xlsx.</span></span><br><span class="line">data &lt;- read.xlsx(<span class="string">&quot;input.xlsx&quot;</span>, sheetIndex = <span class="number">1</span>)</span><br><span class="line">print(data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id     name salary start_date       dept</span><br><span class="line">1  <span class="number">1</span>     Rick <span class="number">623.30</span> <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span>         IT</span><br><span class="line">2  <span class="number">2</span>      Dan <span class="number">515.20</span> <span class="number">2013</span>-<span class="number">09</span>-<span class="number">23</span> Operations</span><br><span class="line">3  <span class="number">3</span> Michelle <span class="number">611.00</span> <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>         IT</span><br><span class="line">4  <span class="number">4</span>     Ryan <span class="number">729.00</span> <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>         HR</span><br><span class="line">5 <span class="literal">NA</span>     Gary <span class="number">843.25</span> <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span>    Finance</span><br><span class="line">6  <span class="number">6</span>     Nina <span class="number">578.00</span> <span class="number">2013</span>-<span class="number">05</span>-<span class="number">21</span>         IT</span><br><span class="line">7  <span class="number">7</span>    Simon <span class="number">632.80</span> <span class="number">2013</span>-<span class="number">07</span>-<span class="number">30</span> Operations</span><br><span class="line">8  <span class="number">8</span>     Guru <span class="number">722.50</span> <span class="number">2014</span>-<span class="number">06</span>-<span class="number">17</span>    Finance</span><br></pre></td></tr></table></figure></p><p>我们可以通过设置 <code>sheetIndex</code> 参数读取指定的工作表。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Read the worksheet named by city in the file input.xlsx.</span></span><br><span class="line">data &lt;- read.xlsx(<span class="string">&quot;input.xlsx&quot;</span>, sheetIndex = <span class="string">&quot;city&quot;</span>)</span><br><span class="line">print(data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">      name    city</span><br><span class="line">1     Rick Seattle</span><br><span class="line">2      Dan   Tampa</span><br><span class="line">3 Michelle Chicago</span><br><span class="line">4     Ryan Seattle</span><br><span class="line">5     Gary Houston</span><br><span class="line">6     Nina  Boston</span><br><span class="line">7    Simon  Mumbai</span><br><span class="line">8     Guru  Dallas</span><br></pre></td></tr></table></figure></p><h3 id="写入-Excel-文件"><a href="#写入-Excel-文件" class="headerlink" title="写入 Excel 文件"></a>写入 Excel 文件</h3><p>R 语言中的 <code>write.xlsx()</code> 函数用于创建 <code>Excel</code> 文件，此文件在工作目录中创建。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Read the first worksheet in the file input.xlsx.</span></span><br><span class="line">data &lt;- read.xlsx(<span class="string">&quot;input.xlsx&quot;</span>, sheetIndex = <span class="number">1</span>)</span><br><span class="line">retval &lt;- subset(data, as.Date(start_date) &gt; as.Date(<span class="string">&quot;2014-01-01&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Write the data into a new file.</span></span><br><span class="line">write.xlsx(retval, file = <span class="string">&quot;output.xlsx&quot;</span>, row.names = <span class="literal">FALSE</span>, sheetName = <span class="string">&quot;salary&quot;</span>)</span><br><span class="line">newdata &lt;- read.xlsx(<span class="string">&quot;output.xlsx&quot;</span>, sheetIndex = <span class="string">&quot;salary&quot;</span>)</span><br><span class="line">print(newdata)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id     name salary start_date    dept</span><br><span class="line">1  <span class="number">3</span> Michelle <span class="number">611.00</span> <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>      IT</span><br><span class="line">2  <span class="number">4</span>     Ryan <span class="number">729.00</span> <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>      HR</span><br><span class="line">3 <span class="literal">NA</span>     Gary <span class="number">843.25</span> <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span> Finance</span><br><span class="line">4  <span class="number">8</span>     Guru <span class="number">722.50</span> <span class="number">2014</span>-<span class="number">06</span>-<span class="number">17</span> Finance</span><br></pre></td></tr></table></figure></p><h2 id="二进制文件"><a href="#二进制文件" class="headerlink" title="二进制文件"></a>二进制文件</h2><p>二进制文件是包含仅以位和字节（<code>0</code> 和 <code>1</code>）的形式存储的信息的文件。它们不是人类可读的，因为它中的字节转换为包含许多其他不可打印字符的字符和符号。尝试使用任何文本编辑器读取二进制文件将显示如 <code>Ø</code> 和 <code>ð</code> 的字符。</p><p>二进制文件必须由特定程序读取才能使用。例如，Microsoft Word 程序的二进制文件只能通过 Word 程序读取到人类可读的形式。这表示，除了人类可读的文本之外，还有更多的信息，例如字符和页码等的格式化，它们也与字母数字字符一起存储。最后一个二进制文件是一个连续的字节序列。我们在文本文件中看到的换行符是连接第一行到下一行的字符。</p><p>有时，由其他程序生成的数据需要由 R 作为二进制文件处理。另外，R 语言是创建可以与其他程序共享的二进制文件所必需的。</p><p>R 语言有两个函数 <code>WriteBin()</code> 和 <code>readBin()</code> 来创建和读取二进制文件。</p><h3 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h3><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">writeBin(object, con)</span><br><span class="line">readBin(con, what, n)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>con</code> 是读取或写入二进制文件的连接对象。</li><li><code>object</code> 是要写入的二进制文件。</li><li><code>what</code> 是像字符，整数等代表字节模式被读取。</li><li><code>n</code> 是从二进制文件读取的字节数。</li></ul><h3 id="写入二进制文件"><a href="#写入二进制文件" class="headerlink" title="写入二进制文件"></a>写入二进制文件</h3><p>我们考虑 R 语言内置数据 <code>mtcars</code>。首先，我们从它创建一个 <code>CSV</code> 文件，并将其转换为二进制文件，并将其存储为操作系统文件。接下来我们读取这个创建的二进制文件。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Read the &quot;mtcars&quot; data frame as a csv file and store only the columns &quot;cyl&quot;, &quot;am&quot; and &quot;gear&quot;.</span></span><br><span class="line">write.table(mtcars, file = <span class="string">&quot;mtcars.csv&quot;</span>, row.names = <span class="literal">FALSE</span>, na = <span class="string">&quot;&quot;</span>, col.names = <span class="literal">TRUE</span>, sep = <span class="string">&quot;,&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Store 5 records from the csv file as a new data frame.</span></span><br><span class="line">new.mtcars &lt;- read.table(<span class="string">&quot;mtcars.csv&quot;</span>, sep = <span class="string">&quot;,&quot;</span>, header = <span class="literal">TRUE</span>, nrows = <span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create a connection object to write the binary file using mode &quot;wb&quot;.</span></span><br><span class="line">write.filename = file(<span class="string">&quot;binmtcars.dat&quot;</span>, <span class="string">&quot;wb&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Write the column names of the data frame to the connection object.</span></span><br><span class="line">writeBin(<span class="built_in">c</span>(<span class="string">&quot;cyl&quot;</span>, <span class="string">&quot;am&quot;</span>, <span class="string">&quot;gear&quot;</span>), write.filename)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Write the records in each of the column to the file.</span></span><br><span class="line">writeBin(<span class="built_in">c</span>(new.mtcars$cyl, new.mtcars$am, new.mtcars$gear), write.filename)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Close the file for writing so that it can be read by other program.</span></span><br><span class="line">close(write.filename)</span><br></pre></td></tr></table></figure></p><h3 id="读取二进制文件"><a href="#读取二进制文件" class="headerlink" title="读取二进制文件"></a>读取二进制文件</h3><p>上面创建的二进制文件将所有数据存储为连续字节。因此，我们将通过选择适当的列名称值和列值来读取它。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a connection object to read the file in binary mode using &quot;rb&quot;.</span></span><br><span class="line">read.filename &lt;- file(<span class="string">&quot;binmtcars.dat&quot;</span>, <span class="string">&quot;rb&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># First read the column names. n = 3 as we have 3 columns.</span></span><br><span class="line">column.names &lt;- readBin(read.filename, character(),  n = <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Next read the column values. n = 18 as we have 3 column names and 15 values.</span></span><br><span class="line">read.filename &lt;- file(<span class="string">&quot;binmtcars.dat&quot;</span>, <span class="string">&quot;rb&quot;</span>)</span><br><span class="line">bindata &lt;- readBin(read.filename, integer(), n = <span class="number">18</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the data.</span></span><br><span class="line">print(bindata)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Read the values from 4th byte to 8th byte which represents &quot;cyl&quot;.</span></span><br><span class="line">cyldata = bindata[<span class="number">4</span>:<span class="number">8</span>]</span><br><span class="line">print(cyldata)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Read the values form 9th byte to 13th byte which represents &quot;am&quot;.</span></span><br><span class="line">amdata = bindata[<span class="number">9</span>:<span class="number">13</span>]</span><br><span class="line">print(amdata)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Read the values form 9th byte to 13th byte which represents &quot;gear&quot;.</span></span><br><span class="line">geardata = bindata[<span class="number">14</span>:<span class="number">18</span>]</span><br><span class="line">print(geardata)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Combine all the read values to a dat frame.</span></span><br><span class="line">finaldata = cbind(cyldata, amdata, geardata)</span><br><span class="line">colnames(finaldata) = column.names</span><br><span class="line">print(finaldata)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果和图表 -</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>]    <span class="number">7108963</span> <span class="number">1728081249</span>    <span class="number">7496037</span>          <span class="number">6</span>          <span class="number">6</span>          <span class="number">4</span></span><br><span class="line">[<span class="number">7</span>]          <span class="number">6</span>          <span class="number">8</span>          <span class="number">1</span>          <span class="number">1</span>          <span class="number">1</span>          <span class="number">0</span></span><br><span class="line">[<span class="number">13</span>]         <span class="number">0</span>          <span class="number">4</span>          <span class="number">4</span>          <span class="number">4</span>          <span class="number">3</span>          <span class="number">3</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">6</span> <span class="number">6</span> <span class="number">4</span> <span class="number">6</span> <span class="number">8</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">1</span> <span class="number">1</span> <span class="number">1</span> <span class="number">0</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">4</span> <span class="number">4</span> <span class="number">4</span> <span class="number">3</span> <span class="number">3</span></span><br><span class="line"></span><br><span class="line">     cyl am gear</span><br><span class="line">[<span class="number">1</span>,]   <span class="number">6</span>  <span class="number">1</span>    <span class="number">4</span></span><br><span class="line">[<span class="number">2</span>,]   <span class="number">6</span>  <span class="number">1</span>    <span class="number">4</span></span><br><span class="line">[<span class="number">3</span>,]   <span class="number">4</span>  <span class="number">1</span>    <span class="number">4</span></span><br><span class="line">[<span class="number">4</span>,]   <span class="number">6</span>  <span class="number">0</span>    <span class="number">3</span></span><br><span class="line">[<span class="number">5</span>,]   <span class="number">8</span>  <span class="number">0</span>    <span class="number">3</span></span><br></pre></td></tr></table></figure></p><p>正如我们所看到的，我们通过读取 R 中的二进制文件得到原始数据。</p><h2 id="XML-文件"><a href="#XML-文件" class="headerlink" title="XML 文件"></a>XML 文件</h2><p><code>XML</code> 是一种文件格式，它使用标准 <code>ASCII</code> 文本共享万维网，内部网和其他地方的文件格式和数据。它代表可扩展标记语言 <code>XML</code>。类似于 <code>HTML</code>，它包含标记标签。但是与 <code>HTML</code> 中的标记标记描述页面的结构不同，在 <code>XML</code> 中，标记描述了包含在文件中的数据的含义。</p><p>您可以使用 <code>XML</code> 包读取 R 语言中的 <code>XML</code> 文件。此软件包可以使用以下命令安装。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">install.packages(<span class="string">&quot;XML&quot;</span>)</span><br></pre></td></tr></table></figure></p><h3 id="创建-XML-文件"><a href="#创建-XML-文件" class="headerlink" title="创建 XML 文件"></a>创建 XML 文件</h3><p>通过将以下数据复制到文本编辑器（如记事本）中来创建文件。使用 <code>.xml</code> 扩展名保存使用记事本中的保存为所有文件​（<code>*.*</code>）​选项，将文件保存为 <code>input.xml</code>。</p><p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">RECORDS</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ID</span>&gt;</span>1<span class="tag">&lt;/<span class="name">ID</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">NAME</span>&gt;</span>Rick<span class="tag">&lt;/<span class="name">NAME</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">SALARY</span>&gt;</span>623.3<span class="tag">&lt;/<span class="name">SALARY</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">STARTDATE</span>&gt;</span>2012-01-01<span class="tag">&lt;/<span class="name">STARTDATE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">DEPT</span>&gt;</span>IT<span class="tag">&lt;/<span class="name">DEPT</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ID</span>&gt;</span>2<span class="tag">&lt;/<span class="name">ID</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">NAME</span>&gt;</span>Dan<span class="tag">&lt;/<span class="name">NAME</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">SALARY</span>&gt;</span>515.2<span class="tag">&lt;/<span class="name">SALARY</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">STARTDATE</span>&gt;</span>2013-09-23<span class="tag">&lt;/<span class="name">STARTDATE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">DEPT</span>&gt;</span>Operations<span class="tag">&lt;/<span class="name">DEPT</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ID</span>&gt;</span>3<span class="tag">&lt;/<span class="name">ID</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">NAME</span>&gt;</span>Michelle<span class="tag">&lt;/<span class="name">NAME</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">SALARY</span>&gt;</span>611<span class="tag">&lt;/<span class="name">SALARY</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">STARTDATE</span>&gt;</span>2014-11-15<span class="tag">&lt;/<span class="name">STARTDATE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">DEPT</span>&gt;</span>IT<span class="tag">&lt;/<span class="name">DEPT</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ID</span>&gt;</span>4<span class="tag">&lt;/<span class="name">ID</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">NAME</span>&gt;</span>Ryan<span class="tag">&lt;/<span class="name">NAME</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">SALARY</span>&gt;</span>729<span class="tag">&lt;/<span class="name">SALARY</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">STARTDATE</span>&gt;</span>2014-05-11<span class="tag">&lt;/<span class="name">STARTDATE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">DEPT</span>&gt;</span>HR<span class="tag">&lt;/<span class="name">DEPT</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ID</span>&gt;</span>5<span class="tag">&lt;/<span class="name">ID</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">NAME</span>&gt;</span>Gary<span class="tag">&lt;/<span class="name">NAME</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">SALARY</span>&gt;</span>843.25<span class="tag">&lt;/<span class="name">SALARY</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">STARTDATE</span>&gt;</span>2015-03-27<span class="tag">&lt;/<span class="name">STARTDATE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">DEPT</span>&gt;</span>Finance<span class="tag">&lt;/<span class="name">DEPT</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ID</span>&gt;</span>6<span class="tag">&lt;/<span class="name">ID</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">NAME</span>&gt;</span>Nina<span class="tag">&lt;/<span class="name">NAME</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">SALARY</span>&gt;</span>578<span class="tag">&lt;/<span class="name">SALARY</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">STARTDATE</span>&gt;</span>2013-05-21<span class="tag">&lt;/<span class="name">STARTDATE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">DEPT</span>&gt;</span>IT<span class="tag">&lt;/<span class="name">DEPT</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ID</span>&gt;</span>7<span class="tag">&lt;/<span class="name">ID</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">NAME</span>&gt;</span>Simon<span class="tag">&lt;/<span class="name">NAME</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">SALARY</span>&gt;</span>632.8<span class="tag">&lt;/<span class="name">SALARY</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">STARTDATE</span>&gt;</span>2013-07-30<span class="tag">&lt;/<span class="name">STARTDATE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">DEPT</span>&gt;</span>Operations<span class="tag">&lt;/<span class="name">DEPT</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ID</span>&gt;</span>8<span class="tag">&lt;/<span class="name">ID</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">NAME</span>&gt;</span>Guru<span class="tag">&lt;/<span class="name">NAME</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">SALARY</span>&gt;</span>722.5<span class="tag">&lt;/<span class="name">SALARY</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">STARTDATE</span>&gt;</span>2014-06-17<span class="tag">&lt;/<span class="name">STARTDATE</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">DEPT</span>&gt;</span>Finance<span class="tag">&lt;/<span class="name">DEPT</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">EMPLOYEE</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">RECORDS</span>&gt;</span></span><br></pre></td></tr></table></figure></p><h3 id="读取-XML-文件"><a href="#读取-XML-文件" class="headerlink" title="读取 XML 文件"></a>读取 XML 文件</h3><p><code>XML</code> 文件由 R 语言使用函数 <code>xmlParse()</code> 读取。它作为列表存储在 R 语言中。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the package required to read XML files.</span></span><br><span class="line">library(<span class="string">&quot;XML&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Also load the other required package.</span></span><br><span class="line">library(<span class="string">&quot;methods&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the input file name to the function.</span></span><br><span class="line">result &lt;- xmlParse(file = <span class="string">&quot;input.xml&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the result.</span></span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">1</span><br><span class="line">Rick</span><br><span class="line">623.3</span><br><span class="line">2012-<span class="number">01</span>-<span class="number">01</span></span><br><span class="line">IT</span><br><span class="line"></span><br><span class="line">2</span><br><span class="line">Dan</span><br><span class="line">515.2</span><br><span class="line">2013-<span class="number">09</span>-<span class="number">23</span></span><br><span class="line">Operations</span><br><span class="line"></span><br><span class="line">3</span><br><span class="line">Michelle</span><br><span class="line">611</span><br><span class="line">2014-<span class="number">11</span>-<span class="number">15</span></span><br><span class="line">IT</span><br><span class="line"></span><br><span class="line">4</span><br><span class="line">Ryan</span><br><span class="line">729</span><br><span class="line">2014-<span class="number">05</span>-<span class="number">11</span></span><br><span class="line">HR</span><br><span class="line"></span><br><span class="line">5</span><br><span class="line">Gary</span><br><span class="line">843.25</span><br><span class="line">2015-<span class="number">03</span>-<span class="number">27</span></span><br><span class="line">Finance</span><br><span class="line"></span><br><span class="line">6</span><br><span class="line">Nina</span><br><span class="line">578</span><br><span class="line">2013-<span class="number">05</span>-<span class="number">21</span></span><br><span class="line">IT</span><br><span class="line"></span><br><span class="line">7</span><br><span class="line">Simon</span><br><span class="line">632.8</span><br><span class="line">2013-<span class="number">07</span>-<span class="number">30</span></span><br><span class="line">Operations</span><br><span class="line"></span><br><span class="line">8</span><br><span class="line">Guru</span><br><span class="line">722.5</span><br><span class="line">2014-<span class="number">06</span>-<span class="number">17</span></span><br><span class="line">Finance</span><br></pre></td></tr></table></figure></p><p><strong>获取 XML 文件中存在的节点数</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the packages required to read XML files.</span></span><br><span class="line">library(<span class="string">&quot;XML&quot;</span>)</span><br><span class="line">library(<span class="string">&quot;methods&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the input file name to the function.</span></span><br><span class="line">result &lt;- xmlParse(file = <span class="string">&quot;input.xml&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Exract the root node form the xml file.</span></span><br><span class="line">rootnode &lt;- xmlRoot(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Find number of nodes in the root.</span></span><br><span class="line">rootsize &lt;- xmlSize(rootnode)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the result.</span></span><br><span class="line">print(rootsize)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">8</span></span><br></pre></td></tr></table></figure></p><p><strong>第一个节点的详细信息</strong></p><p>让我们看看解析文件的第一条记录。它将给我们一个关于存在于顶层节点中的各种元素的想法。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the packages required to read XML files.</span></span><br><span class="line">library(<span class="string">&quot;XML&quot;</span>)</span><br><span class="line">library(<span class="string">&quot;methods&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the input file name to the function.</span></span><br><span class="line">result &lt;- xmlParse(file = <span class="string">&quot;input.xml&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Exract the root node form the xml file.</span></span><br><span class="line">rootnode &lt;- xmlRoot(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the result.</span></span><br><span class="line">print(rootnode[<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">$EMPLOYEE</span><br><span class="line">  <span class="number">1</span></span><br><span class="line">  Rick</span><br><span class="line">  <span class="number">623.3</span></span><br><span class="line">  <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span></span><br><span class="line">  IT</span><br><span class="line"></span><br><span class="line"><span class="built_in">attr</span>(,<span class="string">&quot;class&quot;</span>)</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;XMLInternalNodeList&quot;</span> <span class="string">&quot;XMLNodeList&quot;</span></span><br></pre></td></tr></table></figure></p><p><strong>获取节点的不同元素</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the packages required to read XML files.</span></span><br><span class="line">library(<span class="string">&quot;XML&quot;</span>)</span><br><span class="line">library(<span class="string">&quot;methods&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the input file name to the function.</span></span><br><span class="line">result &lt;- xmlParse(file = <span class="string">&quot;input.xml&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Exract the root node form the xml file.</span></span><br><span class="line">rootnode &lt;- xmlRoot(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the first element of the first node.</span></span><br><span class="line">print(rootnode[[<span class="number">1</span>]][[<span class="number">1</span>]])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the fifth element of the first node.</span></span><br><span class="line">print(rootnode[[<span class="number">1</span>]][[<span class="number">5</span>]])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the second element of the third node.</span></span><br><span class="line">print(rootnode[[<span class="number">3</span>]][[<span class="number">2</span>]])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">1</span><br><span class="line">IT</span><br><span class="line">Michelle</span><br></pre></td></tr></table></figure></p><h3 id="XML-到数据框"><a href="#XML-到数据框" class="headerlink" title="XML 到数据框"></a>XML 到数据框</h3><p>为了在大文件中有效地处理数据，我们将 <code>XML</code> 文件中的数据作为数据框读取。然后处理数据框以进行数据分析。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the packages required to read XML files.</span></span><br><span class="line">library(<span class="string">&quot;XML&quot;</span>)</span><br><span class="line">library(<span class="string">&quot;methods&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Convert the input xml file to a data frame.</span></span><br><span class="line">xmldataframe &lt;- xmlToDataFrame(<span class="string">&quot;input.xml&quot;</span>)</span><br><span class="line">print(xmldataframe)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  ID     NAME SALARY  STARTDATE       DEPT</span><br><span class="line">1  <span class="number">1</span>     Rick  <span class="number">623.3</span> <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span>         IT</span><br><span class="line">2  <span class="number">2</span>      Dan  <span class="number">515.2</span> <span class="number">2013</span>-<span class="number">09</span>-<span class="number">23</span> Operations</span><br><span class="line">3  <span class="number">3</span> Michelle    <span class="number">611</span> <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>         IT</span><br><span class="line">4  <span class="number">4</span>     Ryan    <span class="number">729</span> <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>         HR</span><br><span class="line">5  <span class="number">5</span>     Gary <span class="number">843.25</span> <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span>    Finance</span><br><span class="line">6  <span class="number">6</span>     Nina    <span class="number">578</span> <span class="number">2013</span>-<span class="number">05</span>-<span class="number">21</span>         IT</span><br><span class="line">7  <span class="number">7</span>    Simon  <span class="number">632.8</span> <span class="number">2013</span>-<span class="number">07</span>-<span class="number">30</span> Operations</span><br><span class="line">8  <span class="number">8</span>     Guru  <span class="number">722.5</span> <span class="number">2014</span>-<span class="number">06</span>-<span class="number">17</span>    Finance</span><br></pre></td></tr></table></figure></p><p>由于数据现在可以作为数据框，我们可以使用数据框相关函数来读取和操作文件。</p><h2 id="JSON-文件"><a href="#JSON-文件" class="headerlink" title="JSON 文件"></a>JSON 文件</h2><p><code>JSON</code> 文件以人类可读格式将数据存储为文本。<code>JSON</code> 代表 <code>JavaScript Object Notation</code>。R 可以使用 <code>rjson</code> 包读取 <code>JSON</code> 文件。</p><p>在 R 语言控制台中，您可以发出以下命令来安装 <code>rjson</code> 包。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">install.packages(<span class="string">&quot;rjson&quot;</span>)</span><br></pre></td></tr></table></figure></p><h3 id="创建-JSON-文件"><a href="#创建-JSON-文件" class="headerlink" title="创建 JSON 文件"></a>创建 JSON 文件</h3><p>通过将以下数据复制到文本编辑器（如记事本）中来创建文件。使用 <code>.json</code> 扩展名保存使用记事本中的保存为所有文件​（<code>*.*</code>）选项，将文件保存为 <code>input.json</code>。</p><p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"><span class="attr">&quot;ID&quot;</span>: [<span class="string">&quot;1&quot;</span>, <span class="string">&quot;2&quot;</span>, <span class="string">&quot;3&quot;</span>, <span class="string">&quot;4&quot;</span>, <span class="string">&quot;5&quot;</span>, <span class="string">&quot;6&quot;</span>, <span class="string">&quot;7&quot;</span>, <span class="string">&quot;8&quot;</span>],</span><br><span class="line"><span class="attr">&quot;Name&quot;</span>: [<span class="string">&quot;Rick&quot;</span>, <span class="string">&quot;Dan&quot;</span>, <span class="string">&quot;Michelle&quot;</span>, <span class="string">&quot;Ryan&quot;</span>, <span class="string">&quot;Gary&quot;</span>, <span class="string">&quot;Nina&quot;</span>, <span class="string">&quot;Simon&quot;</span>, <span class="string">&quot;Guru&quot;</span>],</span><br><span class="line"><span class="attr">&quot;Salary&quot;</span>: [<span class="string">&quot;623.3&quot;</span>, <span class="string">&quot;515.2&quot;</span>, <span class="string">&quot;611&quot;</span>, <span class="string">&quot;729&quot;</span>, <span class="string">&quot;843.25&quot;</span>, <span class="string">&quot;578&quot;</span>, <span class="string">&quot;632.8&quot;</span>, <span class="string">&quot;722.5&quot;</span>],</span><br><span class="line"></span><br><span class="line"><span class="attr">&quot;StartDate&quot;</span>: [<span class="string">&quot;2012-01-01&quot;</span>, <span class="string">&quot;2013-09-23&quot;</span>, <span class="string">&quot;2014-11-15&quot;</span>, <span class="string">&quot;2014-05-11&quot;</span>, <span class="string">&quot;2015-03-27&quot;</span>, <span class="string">&quot;2013-05-21&quot;</span>,</span><br><span class="line"><span class="string">&quot;2013-07-30&quot;</span>, <span class="string">&quot;2014-06-17&quot;</span></span><br><span class="line">],</span><br><span class="line"><span class="attr">&quot;Dept&quot;</span>: [<span class="string">&quot;IT&quot;</span>, <span class="string">&quot;Operations&quot;</span>, <span class="string">&quot;IT&quot;</span>, <span class="string">&quot;HR&quot;</span>, <span class="string">&quot;Finance&quot;</span>, <span class="string">&quot;IT&quot;</span>, <span class="string">&quot;Operations&quot;</span>, <span class="string">&quot;Finance&quot;</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="读取-JSON-文件"><a href="#读取-JSON-文件" class="headerlink" title="读取 JSON 文件"></a>读取 JSON 文件</h3><p><code>JSON</code> 文件由 R 使用来自 <code>fromJSON()</code> 的函数读取。它作为列表存储在 R 中。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the package required to read JSON files.</span></span><br><span class="line">library(<span class="string">&quot;rjson&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the input file name to the function.</span></span><br><span class="line">result &lt;- fromJSON(file = <span class="string">&quot;input.json&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the result.</span></span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">$ID</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;1&quot;</span> <span class="string">&quot;2&quot;</span> <span class="string">&quot;3&quot;</span> <span class="string">&quot;4&quot;</span> <span class="string">&quot;5&quot;</span> <span class="string">&quot;6&quot;</span> <span class="string">&quot;7&quot;</span> <span class="string">&quot;8&quot;</span></span><br><span class="line"></span><br><span class="line">$Name</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Rick&quot;</span>     <span class="string">&quot;Dan&quot;</span>      <span class="string">&quot;Michelle&quot;</span> <span class="string">&quot;Ryan&quot;</span>     <span class="string">&quot;Gary&quot;</span>     <span class="string">&quot;Nina&quot;</span>     <span class="string">&quot;Simon&quot;</span>    <span class="string">&quot;Guru&quot;</span>    </span><br><span class="line"></span><br><span class="line">$Salary</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;623.3&quot;</span>  <span class="string">&quot;515.2&quot;</span>  <span class="string">&quot;611&quot;</span>    <span class="string">&quot;729&quot;</span>    <span class="string">&quot;843.25&quot;</span> <span class="string">&quot;578&quot;</span>    <span class="string">&quot;632.8&quot;</span>  <span class="string">&quot;722.5&quot;</span> </span><br><span class="line"></span><br><span class="line">$StartDate</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;2012-01-01&quot;</span> <span class="string">&quot;2013-09-23&quot;</span> <span class="string">&quot;2014-11-15&quot;</span> <span class="string">&quot;2014-05-11&quot;</span> <span class="string">&quot;2015-03-27&quot;</span> <span class="string">&quot;2013-05-21&quot;</span> <span class="string">&quot;2013-07-30&quot;</span> <span class="string">&quot;2014-06-17&quot;</span></span><br><span class="line"></span><br><span class="line">$Dept</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;IT&quot;</span>         <span class="string">&quot;Operations&quot;</span> <span class="string">&quot;IT&quot;</span>         <span class="string">&quot;HR&quot;</span>         <span class="string">&quot;Finance&quot;</span>    <span class="string">&quot;IT&quot;</span>         <span class="string">&quot;Operations&quot;</span> <span class="string">&quot;Finance&quot;</span>   </span><br></pre></td></tr></table></figure></p><h3 id="将-JSON-转换为数据框"><a href="#将-JSON-转换为数据框" class="headerlink" title="将 JSON 转换为数据框"></a>将 JSON 转换为数据框</h3><p>我们可以使用 <code>as.data.frame()</code> 函数将上面提取的数据转换为 R 语言数据框以进行进一步分析。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the package required to read JSON files.</span></span><br><span class="line">library(<span class="string">&quot;rjson&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the input file name to the function.</span></span><br><span class="line">result &lt;- fromJSON(file = <span class="string">&quot;input.json&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Convert JSON file to a data frame.</span></span><br><span class="line">json_data_frame &lt;- as.data.frame(result)</span><br><span class="line"></span><br><span class="line">print(json_data_frame)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  ID     Name Salary  StartDate       Dept</span><br><span class="line">1  <span class="number">1</span>     Rick  <span class="number">623.3</span> <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span>         IT</span><br><span class="line">2  <span class="number">2</span>      Dan  <span class="number">515.2</span> <span class="number">2013</span>-<span class="number">09</span>-<span class="number">23</span> Operations</span><br><span class="line">3  <span class="number">3</span> Michelle    <span class="number">611</span> <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>         IT</span><br><span class="line">4  <span class="number">4</span>     Ryan    <span class="number">729</span> <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>         HR</span><br><span class="line">5  <span class="number">5</span>     Gary <span class="number">843.25</span> <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span>    Finance</span><br><span class="line">6  <span class="number">6</span>     Nina    <span class="number">578</span> <span class="number">2013</span>-<span class="number">05</span>-<span class="number">21</span>         IT</span><br><span class="line">7  <span class="number">7</span>    Simon  <span class="number">632.8</span> <span class="number">2013</span>-<span class="number">07</span>-<span class="number">30</span> Operations</span><br><span class="line">8  <span class="number">8</span>     Guru  <span class="number">722.5</span> <span class="number">2014</span>-<span class="number">06</span>-<span class="number">17</span>    Finance</span><br></pre></td></tr></table></figure></p><h2 id="Web-数据"><a href="#Web-数据" class="headerlink" title="Web 数据"></a>Web 数据</h2><p>许多网站提供数据供其用户使用。例如，世界卫生组织（WHO）以 <code>CSV</code>，<code>txt</code> 和 <code>XML</code> 文件的形式提供健康和医疗信息的报告。使用 R 语言程序，我们可以从这些网站以编程方式提取特定数据。R 语言中用于从网站中提取数据的一些包是 <code>RCurl</code>，<code>XML</code> 和 <code>stringr</code>，它们用于连接到 URL，识别文件所需的链接并将它们下载到本地环境。</p><p>安装 R 语言的包处理 URL 和链接到文件需要以下的包。如果它们在 R 语言环境中不可用，您可以使用以下命令安装它们。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">install.packages(<span class="string">&quot;RCurl&quot;</span>)</span><br><span class="line">install.packages(<span class="string">&quot;XML&quot;</span>)</span><br><span class="line">install.packages(<span class="string">&quot;stringr&quot;</span>)</span><br><span class="line">install.packages(<span class="string">&quot;plyr&quot;</span>)</span><br></pre></td></tr></table></figure></p><h3 id="读取-Web-数据"><a href="#读取-Web-数据" class="headerlink" title="读取 Web 数据"></a>读取 Web 数据</h3><p>我们将访问 <code>URL</code> 天气数据，并使用 R 下载 2015 年的 <code>CSV</code> 文件。</p><p>我们将使用函数 <code>getHTMLLinks()</code> 来收集文件的 <code>URL</code>。然后我们将使用函数<code>downlaod.file()</code> 将文件保存到本地系统。由于我们将对多个文件一次又一次地应用相同的代码，因此我们将创建一个被多次调用的函数。文件名作为参数以 R 列表对象的形式传递到此函数。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Load the package required.</span></span><br><span class="line">library(<span class="string">&quot;RCurl&quot;</span>)</span><br><span class="line">library(<span class="string">&quot;XML&quot;</span>)</span><br><span class="line">library(<span class="string">&quot;stringr&quot;</span>)</span><br><span class="line">library(<span class="string">&quot;plyr&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Read the URL.</span></span><br><span class="line">url &lt;- <span class="string">&quot;https://www.geos.ed.ac.uk/~weather/jcmb_ws/&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Gather the html links present in the webpage.</span></span><br><span class="line">links &lt;- getHTMLLinks(url)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Identify only the links which point to the JCMB 2015 files. </span></span><br><span class="line">filenames &lt;- links[str_detect(links, <span class="string">&quot;JCMB_2015&quot;</span>)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Store the file names as a list.</span></span><br><span class="line">filenames_list &lt;- as.list(filenames)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create a function to download the files by passing the URL and filename list.</span></span><br><span class="line">downloadcsv &lt;- <span class="keyword">function</span> (mainurl, filename) &#123;</span><br><span class="line">   filedetails &lt;- str_c(mainurl, filename)</span><br><span class="line">   download.file(filedetails, filename)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Now apply the l_ply function and save the files into the current R working directory.</span></span><br><span class="line">l_ply(filenames, downloadcsv, mainurl = <span class="string">&quot;https://www.geos.ed.ac.uk/~weather/jcmb_ws/&quot;</span>)</span><br></pre></td></tr></table></figure></p><p><strong>验证文件下载</strong></p><p>运行上述代码后，您可以在当前 R 语言工作目录中找到以下文件。<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">JCMB<span class="emphasis">_2015.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Apr.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Aug.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Dec.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Feb.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Jan.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Jul.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Jun.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Mar.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_May.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Nov.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Oct.csv</span></span><br><span class="line"><span class="emphasis">JCMB_</span>2015<span class="emphasis">_Sep.csv</span></span><br></pre></td></tr></table></figure></p><h2 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h2><p>数据是关系数据库系统以规范化格式存储。因此，要进行统计计算，我们将需要非常先进和复杂的 <code>SQL</code> 查询。但 R 语言可以轻松地连接到许多关系数据库，如 <code>MySQL</code>，<code>Oracle</code>，<code>SQL Server</code> 等，并从它们获取记录作为数据框。一旦数据在 R 语言环境中可用，它就变成正常的 R 语言数据集，并且可以使用所有强大的包和函数来操作或分析。</p><p>在本教程中，我们将使用 MySQL 作为连接到 R 语言的参考数据库。</p><p>R 语言有一个名为 <code>RMySQL</code> 的内置包，它提供与 MySQL 数据库之间的本地连接。您可以使用以下命令在 R 语言环境中安装此软件包。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">install.packages(<span class="string">&quot;RMySQL&quot;</span>)</span><br></pre></td></tr></table></figure></p><h3 id="连接到-MySQL"><a href="#连接到-MySQL" class="headerlink" title="连接到 MySQL"></a>连接到 MySQL</h3><p>一旦安装了包，我们在 R 中创建一个连接对象以连接到数据库。它使用用户名，密码，数据库名称和主机名作为输入。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a connection Object to MySQL database.</span></span><br><span class="line"><span class="comment"># We will connect to the sampel database named &quot;testdb&quot; that comes with MySQL installation.</span></span><br><span class="line">library(<span class="string">&quot;RMySQL&quot;</span>)</span><br><span class="line">conn = dbConnect(MySQL(), user = <span class="string">&#x27;root&#x27;</span>, password = <span class="string">&#x27;root&#x27;</span>, dbname = <span class="string">&#x27;testdb&#x27;</span>, host = <span class="string">&#x27;localhost&#x27;</span>, port=<span class="number">3306</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Set the encoding method to gbk</span></span><br><span class="line">dbSendQuery(conn, <span class="string">&#x27;SET NAMES gbk&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># List the tables available in this database.</span></span><br><span class="line">dbListTables(conn)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;employee_tbl&quot;</span>    <span class="string">&quot;tbl&quot;</span>    <span class="string">&quot;tcount_tbl&quot;</span>    <span class="string">&quot;transaction_test&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="查询表数据"><a href="#查询表数据" class="headerlink" title="查询表数据"></a>查询表数据</h3><p>我们可以使用函数 <code>dbSendQuery()</code> 查询 MySQL 中的数据库表。查询在 MySQL 中执行，并使用 R 语言 <code>fetch()</code> 函数返回结果集。最后，它被存储为 R 语言中的数据框。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Query the &quot;tbl&quot; tables to get all the rows.</span></span><br><span class="line">result = dbSendQuery(conn, <span class="string">&quot;SELECT * FROM tbl&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Store the result in a R data frame object. n = 6 is used to fetch first 6 rows.</span></span><br><span class="line">data.frame = fetch(result, n = <span class="number">6</span>)</span><br><span class="line">print(data.frame)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id       title author submission_date</span><br><span class="line">1  <span class="number">1</span>    学习 PHP    PHP      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">12</span></span><br><span class="line">2  <span class="number">2</span>  学习 MySQL  MySQL      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">12</span></span><br><span class="line">3  <span class="number">3</span>    学习 C++    C++      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">01</span></span><br><span class="line">4  <span class="number">4</span> 学习 Python Python      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">01</span></span><br><span class="line">5  <span class="number">5</span>  MySQL 教程  MySQL      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">12</span></span><br><span class="line">6  <span class="number">6</span>   JAVA 教程   JAVA      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">12</span></span><br></pre></td></tr></table></figure></p><p><strong>带过滤条件的查询</strong></p><p>我们可以传递任何有效的 <code>SELECT</code> 查询来获取结果。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Query the &quot;tbl&quot; tables to get all the rows with author equal to &#x27;MySQL&#x27;.</span></span><br><span class="line">result = dbSendQuery(conn, <span class="string">&quot;SELECT * FROM tbl WHERE author = &#x27;MySQL&#x27;&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Fetch all the records and store it as a data frame.</span></span><br><span class="line">data.frame = fetch(result)</span><br><span class="line">print(data.frame)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  id      title author submission_date</span><br><span class="line">1  <span class="number">2</span> 学习 MySQL  MySQL      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">12</span></span><br><span class="line">2  <span class="number">5</span> MySQL 教程  MySQL      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">12</span></span><br></pre></td></tr></table></figure></p><h3 id="更新表数据"><a href="#更新表数据" class="headerlink" title="更新表数据"></a>更新表数据</h3><p>我们可以通过将更新查询传递给 <code>dbSendQuery()</code> 函数来更新 MySQL 表中的行。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Query the &quot;tbl&quot; tables to get the rows with id equal to 1.</span></span><br><span class="line">result = dbSendQuery(conn, <span class="string">&quot;SELECT * FROM tbl WHERE id = 1&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Fetch all the records and store it as a data frame.</span></span><br><span class="line">data.frame = fetch(result)</span><br><span class="line">print(data.frame)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Update the record with id equal to 1.</span></span><br><span class="line">dbSendQuery(conn, <span class="string">&quot;UPDATE tbl SET submission_date = &#x27;2000-01-12&#x27; WHERE id = 1&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Query the &quot;tbl&quot; tables to get the rows with id equal to 1.</span></span><br><span class="line">result = dbSendQuery(conn, <span class="string">&quot;SELECT * FROM tbl WHERE id = 1&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Fetch all the records and store it as a data frame.</span></span><br><span class="line">data.frame = fetch(result)</span><br><span class="line">print(data.frame)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在执行上面的代码后，我们可以看到在 MySQL 环境中更新的表。</span></span><br><span class="line"></span><br><span class="line">  id    title author submission_date</span><br><span class="line">1  <span class="number">1</span> 学习 PHP    PHP      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">12</span></span><br><span class="line"></span><br><span class="line">  id    title author submission_date</span><br><span class="line">1  <span class="number">1</span> 学习 PHP    PHP      <span class="number">2000</span>-<span class="number">01</span>-<span class="number">12</span></span><br></pre></td></tr></table></figure></p><h3 id="向表插入数据"><a href="#向表插入数据" class="headerlink" title="向表插入数据"></a>向表插入数据</h3><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Insert data to the &quot;tbl&quot; tables</span></span><br><span class="line">dbSendQuery(conn,</span><br><span class="line">   <span class="string">&quot;INSERT INTO tbl (title, author, submission_date) VALUES(&#x27;学习 R&#x27;, &#x27;R&#x27;, &#x27;2021-01-22&#x27;)&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Query the &quot;tbl&quot; tables to get all the rows.</span></span><br><span class="line">result = dbSendQuery(conn, <span class="string">&quot;SELECT * FROM tbl&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Fetch all the records and store it as a data frame.</span></span><br><span class="line">data.frame = fetch(result)</span><br><span class="line">print(data.frame)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在执行上面的代码后，我们可以看到插入到 MySQL 环境中的表中的行。</span></span><br><span class="line"></span><br><span class="line">  id       title author submission_date</span><br><span class="line">1  <span class="number">1</span>    学习 PHP    PHP      <span class="number">2000</span>-<span class="number">01</span>-<span class="number">12</span></span><br><span class="line">2  <span class="number">2</span>  学习 MySQL  MySQL      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">12</span></span><br><span class="line">3  <span class="number">3</span>    学习 C++    C++      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">01</span></span><br><span class="line">4  <span class="number">4</span> 学习 Python Python      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">01</span></span><br><span class="line">5  <span class="number">5</span>  MySQL 教程  MySQL      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">12</span></span><br><span class="line">6  <span class="number">6</span>   JAVA 教程   JAVA      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">12</span></span><br><span class="line">7  <span class="number">7</span>      学习 R      R      <span class="number">2021</span>-<span class="number">01</span>-<span class="number">22</span></span><br></pre></td></tr></table></figure></p><h3 id="创建表"><a href="#创建表" class="headerlink" title="创建表"></a>创建表</h3><p>我们可以在 MySQL 中使用函数 <code>dbWriteTable()</code> 创建表。如果表已经存在，它将覆盖该表，并将数据框用作输入。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the connection object to the database where we want to create the table.</span></span><br><span class="line">conn = dbConnect(MySQL(), user = <span class="string">&#x27;root&#x27;</span>, password = <span class="string">&#x27;root&#x27;</span>, dbname = <span class="string">&#x27;testdb&#x27;</span>, host = <span class="string">&#x27;localhost&#x27;</span>, port = <span class="string">&#x27;3306&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Use the R data frame &quot;mtcars&quot; to create the table in MySQL.</span></span><br><span class="line"><span class="comment"># All the rows of mtcars are taken inot MySQL.</span></span><br><span class="line">dbWriteTable(conn, <span class="string">&quot;mtcars&quot;</span>, mtcars[, ], overwrite = <span class="literal">TRUE</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># List the tables available in this database.</span></span><br><span class="line">dbListTables(conn)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Query the &quot;mtcars&quot; tables to get the rows</span></span><br><span class="line">result = dbSendQuery(conn, <span class="string">&quot;SELECT * FROM mtcars&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Fetch all the records and store it as a data frame.</span></span><br><span class="line">data.frame = fetch(result)</span><br><span class="line">print(data.frame)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行上面的代码后，我们可以看到在 MySQL 环境中创建的表。</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="literal">TRUE</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;employee_tbl&quot;</span>    <span class="string">&quot;tbl&quot;</span>    <span class="string">&quot;tcount_tbl&quot;</span>    <span class="string">&quot;transaction_test&quot;</span></span><br><span class="line"></span><br><span class="line">             row_names  mpg cyl  disp  hp drat    wt  qsec vs am gear carb</span><br><span class="line">1            Mazda RX4 <span class="number">21.0</span>   <span class="number">6</span> <span class="number">160.0</span> <span class="number">110</span> <span class="number">3.90</span> <span class="number">2.620</span> <span class="number">16.46</span>  <span class="number">0</span>  <span class="number">1</span>    <span class="number">4</span>    <span class="number">4</span></span><br><span class="line">2        Mazda RX4 Wag <span class="number">21.0</span>   <span class="number">6</span> <span class="number">160.0</span> <span class="number">110</span> <span class="number">3.90</span> <span class="number">2.875</span> <span class="number">17.02</span>  <span class="number">0</span>  <span class="number">1</span>    <span class="number">4</span>    <span class="number">4</span></span><br><span class="line">3           Datsun <span class="number">710</span> <span class="number">22.8</span>   <span class="number">4</span> <span class="number">108.0</span>  <span class="number">93</span> <span class="number">3.85</span> <span class="number">2.320</span> <span class="number">18.61</span>  <span class="number">1</span>  <span class="number">1</span>    <span class="number">4</span>    <span class="number">1</span></span><br><span class="line">4       Hornet <span class="number">4</span> Drive <span class="number">21.4</span>   <span class="number">6</span> <span class="number">258.0</span> <span class="number">110</span> <span class="number">3.08</span> <span class="number">3.215</span> <span class="number">19.44</span>  <span class="number">1</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">1</span></span><br><span class="line">5    Hornet Sportabout <span class="number">18.7</span>   <span class="number">8</span> <span class="number">360.0</span> <span class="number">175</span> <span class="number">3.15</span> <span class="number">3.440</span> <span class="number">17.02</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">2</span></span><br><span class="line">6              Valiant <span class="number">18.1</span>   <span class="number">6</span> <span class="number">225.0</span> <span class="number">105</span> <span class="number">2.76</span> <span class="number">3.460</span> <span class="number">20.22</span>  <span class="number">1</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">1</span></span><br><span class="line">7           Duster <span class="number">360</span> <span class="number">14.3</span>   <span class="number">8</span> <span class="number">360.0</span> <span class="number">245</span> <span class="number">3.21</span> <span class="number">3.570</span> <span class="number">15.84</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">4</span></span><br><span class="line">8            Merc <span class="number">240</span>D <span class="number">24.4</span>   <span class="number">4</span> <span class="number">146.7</span>  <span class="number">62</span> <span class="number">3.69</span> <span class="number">3.190</span> <span class="number">20.00</span>  <span class="number">1</span>  <span class="number">0</span>    <span class="number">4</span>    <span class="number">2</span></span><br><span class="line">9             Merc <span class="number">230</span> <span class="number">22.8</span>   <span class="number">4</span> <span class="number">140.8</span>  <span class="number">95</span> <span class="number">3.92</span> <span class="number">3.150</span> <span class="number">22.90</span>  <span class="number">1</span>  <span class="number">0</span>    <span class="number">4</span>    <span class="number">2</span></span><br><span class="line">10            Merc <span class="number">280</span> <span class="number">19.2</span>   <span class="number">6</span> <span class="number">167.6</span> <span class="number">123</span> <span class="number">3.92</span> <span class="number">3.440</span> <span class="number">18.30</span>  <span class="number">1</span>  <span class="number">0</span>    <span class="number">4</span>    <span class="number">4</span></span><br><span class="line">11           Merc <span class="number">280</span>C <span class="number">17.8</span>   <span class="number">6</span> <span class="number">167.6</span> <span class="number">123</span> <span class="number">3.92</span> <span class="number">3.440</span> <span class="number">18.90</span>  <span class="number">1</span>  <span class="number">0</span>    <span class="number">4</span>    <span class="number">4</span></span><br><span class="line">12          Merc <span class="number">450</span>SE <span class="number">16.4</span>   <span class="number">8</span> <span class="number">275.8</span> <span class="number">180</span> <span class="number">3.07</span> <span class="number">4.070</span> <span class="number">17.40</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">3</span></span><br><span class="line">13          Merc <span class="number">450</span>SL <span class="number">17.3</span>   <span class="number">8</span> <span class="number">275.8</span> <span class="number">180</span> <span class="number">3.07</span> <span class="number">3.730</span> <span class="number">17.60</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">3</span></span><br><span class="line">14         Merc <span class="number">450</span>SLC <span class="number">15.2</span>   <span class="number">8</span> <span class="number">275.8</span> <span class="number">180</span> <span class="number">3.07</span> <span class="number">3.780</span> <span class="number">18.00</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">3</span></span><br><span class="line">15  Cadillac Fleetwood <span class="number">10.4</span>   <span class="number">8</span> <span class="number">472.0</span> <span class="number">205</span> <span class="number">2.93</span> <span class="number">5.250</span> <span class="number">17.98</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">4</span></span><br><span class="line">16 Lincoln Continental <span class="number">10.4</span>   <span class="number">8</span> <span class="number">460.0</span> <span class="number">215</span> <span class="number">3.00</span> <span class="number">5.424</span> <span class="number">17.82</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">4</span></span><br><span class="line">17   Chrysler Imperial <span class="number">14.7</span>   <span class="number">8</span> <span class="number">440.0</span> <span class="number">230</span> <span class="number">3.23</span> <span class="number">5.345</span> <span class="number">17.42</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">4</span></span><br><span class="line">18            Fiat <span class="number">128</span> <span class="number">32.4</span>   <span class="number">4</span>  <span class="number">78.7</span>  <span class="number">66</span> <span class="number">4.08</span> <span class="number">2.200</span> <span class="number">19.47</span>  <span class="number">1</span>  <span class="number">1</span>    <span class="number">4</span>    <span class="number">1</span></span><br><span class="line">19         Honda Civic <span class="number">30.4</span>   <span class="number">4</span>  <span class="number">75.7</span>  <span class="number">52</span> <span class="number">4.93</span> <span class="number">1.615</span> <span class="number">18.52</span>  <span class="number">1</span>  <span class="number">1</span>    <span class="number">4</span>    <span class="number">2</span></span><br><span class="line">20      Toyota Corolla <span class="number">33.9</span>   <span class="number">4</span>  <span class="number">71.1</span>  <span class="number">65</span> <span class="number">4.22</span> <span class="number">1.835</span> <span class="number">19.90</span>  <span class="number">1</span>  <span class="number">1</span>    <span class="number">4</span>    <span class="number">1</span></span><br><span class="line">21       Toyota Corona <span class="number">21.5</span>   <span class="number">4</span> <span class="number">120.1</span>  <span class="number">97</span> <span class="number">3.70</span> <span class="number">2.465</span> <span class="number">20.01</span>  <span class="number">1</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">1</span></span><br><span class="line">22    Dodge Challenger <span class="number">15.5</span>   <span class="number">8</span> <span class="number">318.0</span> <span class="number">150</span> <span class="number">2.76</span> <span class="number">3.520</span> <span class="number">16.87</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">2</span></span><br><span class="line">23         AMC Javelin <span class="number">15.2</span>   <span class="number">8</span> <span class="number">304.0</span> <span class="number">150</span> <span class="number">3.15</span> <span class="number">3.435</span> <span class="number">17.30</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">2</span></span><br><span class="line">24          Camaro Z28 <span class="number">13.3</span>   <span class="number">8</span> <span class="number">350.0</span> <span class="number">245</span> <span class="number">3.73</span> <span class="number">3.840</span> <span class="number">15.41</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">4</span></span><br><span class="line">25    Pontiac Firebird <span class="number">19.2</span>   <span class="number">8</span> <span class="number">400.0</span> <span class="number">175</span> <span class="number">3.08</span> <span class="number">3.845</span> <span class="number">17.05</span>  <span class="number">0</span>  <span class="number">0</span>    <span class="number">3</span>    <span class="number">2</span></span><br><span class="line">26           Fiat X1-<span class="number">9</span> <span class="number">27.3</span>   <span class="number">4</span>  <span class="number">79.0</span>  <span class="number">66</span> <span class="number">4.08</span> <span class="number">1.935</span> <span class="number">18.90</span>  <span class="number">1</span>  <span class="number">1</span>    <span class="number">4</span>    <span class="number">1</span></span><br><span class="line">27       Porsche <span class="number">914</span>-<span class="number">2</span> <span class="number">26.0</span>   <span class="number">4</span> <span class="number">120.3</span>  <span class="number">91</span> <span class="number">4.43</span> <span class="number">2.140</span> <span class="number">16.70</span>  <span class="number">0</span>  <span class="number">1</span>    <span class="number">5</span>    <span class="number">2</span></span><br><span class="line">28        Lotus Europa <span class="number">30.4</span>   <span class="number">4</span>  <span class="number">95.1</span> <span class="number">113</span> <span class="number">3.77</span> <span class="number">1.513</span> <span class="number">16.90</span>  <span class="number">1</span>  <span class="number">1</span>    <span class="number">5</span>    <span class="number">2</span></span><br><span class="line">29      Ford Pantera L <span class="number">15.8</span>   <span class="number">8</span> <span class="number">351.0</span> <span class="number">264</span> <span class="number">4.22</span> <span class="number">3.170</span> <span class="number">14.50</span>  <span class="number">0</span>  <span class="number">1</span>    <span class="number">5</span>    <span class="number">4</span></span><br><span class="line">30        Ferrari Dino <span class="number">19.7</span>   <span class="number">6</span> <span class="number">145.0</span> <span class="number">175</span> <span class="number">3.62</span> <span class="number">2.770</span> <span class="number">15.50</span>  <span class="number">0</span>  <span class="number">1</span>    <span class="number">5</span>    <span class="number">6</span></span><br><span class="line">31       Maserati Bora <span class="number">15.0</span>   <span class="number">8</span> <span class="number">301.0</span> <span class="number">335</span> <span class="number">3.54</span> <span class="number">3.570</span> <span class="number">14.60</span>  <span class="number">0</span>  <span class="number">1</span>    <span class="number">5</span>    <span class="number">8</span></span><br><span class="line">32          Volvo <span class="number">142</span>E <span class="number">21.4</span>   <span class="number">4</span> <span class="number">121.0</span> <span class="number">109</span> <span class="number">4.11</span> <span class="number">2.780</span> <span class="number">18.60</span>  <span class="number">1</span>  <span class="number">1</span>    <span class="number">4</span>    <span class="number">2</span></span><br></pre></td></tr></table></figure></p><h3 id="删除表"><a href="#删除表" class="headerlink" title="删除表"></a>删除表</h3><p>我们可以删除 MySQL 数据库中的表，将 <code>DROP TABLE</code> 语句传递到 <code>dbSendQuery()</code> 中，就像我们使用它查询表中的数据一样。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># List the tables available in this database.</span></span><br><span class="line">dbListTables(conn)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Delete the &quot;mtcars&quot; table.</span></span><br><span class="line">dbSendQuery(conn, <span class="string">&#x27;DROP TABLE IF EXISTS mtcars&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># List the tables available in this database.</span></span><br><span class="line">dbListTables(conn)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行上面的代码后，我们可以看到表在 MySQL 环境中被删除。</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;employee_tbl&quot;</span>     <span class="string">&quot;mtcars&quot;</span>           <span class="string">&quot;tbl&quot;</span>              <span class="string">&quot;tcount_tbl&quot;</span>       <span class="string">&quot;transaction_test&quot;</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;employee_tbl&quot;</span>     <span class="string">&quot;tbl&quot;</span>              <span class="string">&quot;tcount_tbl&quot;</span>       <span class="string">&quot;transaction_test&quot;</span></span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/R/">R</category>
      
      
      <comments>https://blog.eurkon.com/post/996a1090.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>R 语言图表</title>
      <link>https://blog.eurkon.com/post/b35ac98a.html</link>
      <guid>https://blog.eurkon.com/post/b35ac98a.html</guid>
      <pubDate>Wed, 20 Jan 2021 08:04:45 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;R 提供了非常丰富的绘图功能，可以通过命令：&lt;code&gt;demo(graphics)&lt;/code&gt;（二维图表） 或者 &lt;code&gt;demo(</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>R 提供了非常丰富的绘图功能，可以通过命令：<code>demo(graphics)</code>（二维图表） 或者 <code>demo(persp)</code>（三维图表）来体验 R 绘图功能的强大。</p><p>图形工具是 R 环境的一个重要组成部分。R 提供了多种绘图相关的命令，分成三类：</p><ul><li><strong>高级绘图命令</strong>：在图形设备上产生一个新的图区，它可能包括坐标轴，标签，标题等等。</li><li><strong>低级绘图命令</strong>：在一个已经存在的图上加上更多的图形元素，如额外的点，线和标签。</li><li><strong>交互式图形命令</strong>：允许交互式地用鼠标在一个已经存在的图上添加图形信息或者提取图形信息。</li></ul><p>下面介绍 R 的常用图表。</p><h2 id="条形图"><a href="#条形图" class="headerlink" title="条形图"></a>条形图</h2><p>条形图表示矩形条中的数据，条的长度与变量的值成比例。R 语言使用函数 <code>barplot()</code> 创建条形图。R 语言可以在条形图中绘制垂直和水平条。在条形图中，每个条可以给予不同的颜色。</p><h3 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h3><p>在 R 语言中创建条形图的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">barplot(H, xlab, ylab, main, names.arg, col)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>H</code> 是包含在条形图中使用的数值的向量或矩阵。</li><li><code>xlab</code> 是 <code>x</code> 轴的标签。</li><li><code>ylab</code> 是 <code>y</code> 轴的标签。</li><li><code>main</code> 是条形图的标题。</li><li><code>names.arg</code> 是在每个条下出现的名称的向量。</li><li><code>col</code> 用于向图中的条形提供颜色。</li></ul><h3 id="创建条形图"><a href="#创建条形图" class="headerlink" title="创建条形图"></a>创建条形图</h3><p>使用输入向量和每个条的名称创建一个简单的条形图。</p><p>以下脚本将创建并保存当前 R 语言工作目录中的条形图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data for the chart.</span></span><br><span class="line">H &lt;- <span class="built_in">c</span>(<span class="number">7</span>, <span class="number">12</span>, <span class="number">28</span>, <span class="number">3</span>, <span class="number">41</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;barchart.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the bar chart.</span></span><br><span class="line">barplot(H)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/barchart.png" alt="条形图"></p><h3 id="条形图标签，标题和颜色"><a href="#条形图标签，标题和颜色" class="headerlink" title="条形图标签，标题和颜色"></a>条形图标签，标题和颜色</h3><p>可以通过添加更多参数来扩展条形图的功能。主要参数用于添加标题。<code>col</code> 参数用于向条形添加颜色。<code>name.args</code> 是具有与输入向量相同数量的值的向量，以描述每个条的含义。</p><p>以下脚本将在当前 R 语言工作目录中创建并保存条形图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data for the chart.</span></span><br><span class="line">H &lt;- <span class="built_in">c</span>(<span class="number">7</span>, <span class="number">12</span>, <span class="number">28</span>, <span class="number">3</span>, <span class="number">41</span>)</span><br><span class="line">M &lt;- <span class="built_in">c</span>(<span class="string">&quot;Mar&quot;</span>, <span class="string">&quot;Apr&quot;</span>, <span class="string">&quot;May&quot;</span>, <span class="string">&quot;Jun&quot;</span>, <span class="string">&quot;Jul&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;barchart_months_revenue.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the bar chart.</span></span><br><span class="line">barplot(H, names.arg = M, xlab = <span class="string">&quot;Month&quot;</span>, ylab = <span class="string">&quot;Revenue&quot;</span>, col = <span class="string">&quot;blue&quot;</span>, main = <span class="string">&quot;Revenue chart&quot;</span>, border = <span class="string">&quot;red&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/barchart_months_revenue.png" alt="条形图标签，标题和颜色"></p><h3 id="组合条形图和堆积条形图"><a href="#组合条形图和堆积条形图" class="headerlink" title="组合条形图和堆积条形图"></a>组合条形图和堆积条形图</h3><p>我们可以使用矩阵作为输入值，在每个条中创建条形图和堆叠组的条形图。</p><p>超过两个变量表示为用于创建组合条形图和堆叠条形图的矩阵。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the input vectors.</span></span><br><span class="line">colors &lt;- <span class="built_in">c</span>(<span class="string">&quot;green&quot;</span>, <span class="string">&quot;orange&quot;</span>, <span class="string">&quot;brown&quot;</span>)</span><br><span class="line">months &lt;- <span class="built_in">c</span>(<span class="string">&quot;Mar&quot;</span>, <span class="string">&quot;Apr&quot;</span>, <span class="string">&quot;May&quot;</span>, <span class="string">&quot;Jun&quot;</span>, <span class="string">&quot;Jul&quot;</span>)</span><br><span class="line">regions &lt;- <span class="built_in">c</span>(<span class="string">&quot;East&quot;</span>, <span class="string">&quot;West&quot;</span>, <span class="string">&quot;North&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the matrix of the values.</span></span><br><span class="line">Values &lt;- matrix(<span class="built_in">c</span>(<span class="number">2</span>, <span class="number">9</span>, <span class="number">3</span>, <span class="number">11</span>, <span class="number">9</span>, <span class="number">4</span>, <span class="number">8</span>, <span class="number">7</span>, <span class="number">3</span>, <span class="number">12</span>, <span class="number">5</span>, <span class="number">2</span>, <span class="number">8</span>, <span class="number">10</span>, <span class="number">11</span>), nrow = <span class="number">3</span>, ncol = <span class="number">5</span>, byrow = <span class="literal">TRUE</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;barchart_stacked.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the bar chart.</span></span><br><span class="line">barplot(Values, main = <span class="string">&quot;total revenue&quot;</span>, names.arg = months, xlab = <span class="string">&quot;month&quot;</span>, ylab = <span class="string">&quot;revenue&quot;</span>, col = colors)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Add the legend to the chart.</span></span><br><span class="line">legend(<span class="string">&quot;topleft&quot;</span>, regions, cex = <span class="number">1.3</span>, fill = colors)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/barchart_stacked.png" alt="组合条形图和堆积条形图"></p><h2 id="箱线图"><a href="#箱线图" class="headerlink" title="箱线图"></a>箱线图</h2><p>箱线图是数据集中的数据分布良好的度量。它将数据集分成三个四分位数。此图表表示数据集中的最小值，最大值，中值，第一四分位数和第三四分位数。它还可用于通过绘制每个数据集的箱线图来比较数据集之间的数据分布。</p><p>R 语言中使用 <code>boxplot()</code> 函数来创建箱线图。</p><h3 id="语法-1"><a href="#语法-1" class="headerlink" title="语法"></a>语法</h3><p>在 R 语言中创建箱线图的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">boxplot(x, data, notch, varwidth, <span class="built_in">names</span>, main)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是向量或公式。</li><li><code>data</code> 是数据框。</li><li><code>notch</code> 是逻辑值。设置为 <code>TRUE</code> 以绘制凹口。</li><li><code>varwidth</code> 是一个逻辑值。设置为 <code>TRUE</code> 以绘制与样本大小成比例的框的宽度。</li><li><code>names</code> 是将打印在每个箱线图下的组标签。</li><li><code>main</code> 用于给图表标题。</li></ul><p>我们使用 R 语言环境中可用的数据集 <code>mtcars</code> 来创建基本箱线图。让我们看看 <code>mtcars</code> 中的列 <code>mpg</code> 和 <code>cyl</code>。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">input &lt;- mtcars[, <span class="built_in">c</span>(<span class="string">&#x27;mpg&#x27;</span>, <span class="string">&#x27;cyl&#x27;</span>)]</span><br><span class="line">print(head(input))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">                   mpg  cyl</span><br><span class="line">Mazda RX4         <span class="number">21.0</span>   <span class="number">6</span></span><br><span class="line">Mazda RX4 Wag     <span class="number">21.0</span>   <span class="number">6</span></span><br><span class="line">Datsun <span class="number">710</span>        <span class="number">22.8</span>   <span class="number">4</span></span><br><span class="line">Hornet <span class="number">4</span> Drive    <span class="number">21.4</span>   <span class="number">6</span></span><br><span class="line">Hornet Sportabout <span class="number">18.7</span>   <span class="number">8</span></span><br><span class="line">Valiant           <span class="number">18.1</span>   <span class="number">6</span></span><br></pre></td></tr></table></figure></p><h3 id="创建箱线图"><a href="#创建箱线图" class="headerlink" title="创建箱线图"></a>创建箱线图</h3><p>以下脚本将为 <code>mpg</code>（英里 / 加仑）和 <code>cyl</code>（气缸数）之间的关系创建箱线图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;boxplot.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the chart.</span></span><br><span class="line">boxplot(mpg ~ cyl, data = mtcars, xlab = <span class="string">&quot;Number of Cylinders&quot;</span>, ylab = <span class="string">&quot;Miles Per Gallon&quot;</span>, main = <span class="string">&quot;Mileage Data&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/boxplot.png" alt="箱线图"></p><h3 id="带槽的箱线图"><a href="#带槽的箱线图" class="headerlink" title="带槽的箱线图"></a>带槽的箱线图</h3><p>我们可以绘制带槽的箱线图，以了解不同数据组的中值如何相互匹配。以下脚本将为每个数据组创建一个带缺口的箱线图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;boxplot_with_notch.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the chart.</span></span><br><span class="line">boxplot(mpg ~ cyl, data = mtcars, xlab = <span class="string">&quot;Number of Cylinders&quot;</span>, ylab = <span class="string">&quot;Miles Per Gallon&quot;</span>, main = <span class="string">&quot;Mileage Data&quot;</span>, notch = <span class="literal">TRUE</span>, varwidth = <span class="literal">TRUE</span>, col = <span class="built_in">c</span>(<span class="string">&quot;green&quot;</span>, <span class="string">&quot;yellow&quot;</span>, <span class="string">&quot;purple&quot;</span>), <span class="built_in">names</span> = <span class="built_in">c</span>(<span class="string">&quot;High&quot;</span>, <span class="string">&quot;Medium&quot;</span>, <span class="string">&quot;Low&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/boxplot_with_notch.png" alt="带槽的箱线图"></p><h2 id="直方图"><a href="#直方图" class="headerlink" title="直方图"></a>直方图</h2><p>直方图表示被存储到范围中的变量的值的频率。直方图类似于条形图，但不同之处在于将值分组为连续范围。直方图中的每个柱表示该范围中存在的值的数量的高度。</p><p>R 语言使用 <code>hist()</code> 函数创建直方图。此函数使用向量作为输入，并使用一些更多的参数来绘制直方图。</p><h3 id="语法-2"><a href="#语法-2" class="headerlink" title="语法"></a>语法</h3><p>使用 R 语言创建直方图的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hist(v, main, xlab, xlim, ylim, breaks, col, border)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>v</code> 是包含直方图中使用的数值的向量。</li><li><code>main</code> 表示图表的标题。</li><li><code>col</code> 用于设置条的颜色。</li><li><code>border</code> 用于设置每个条的边框颜色。</li><li><code>xlab</code> 用于给出 <code>x</code> 轴的描述。</li><li><code>xlim</code> 用于指定 <code>x</code> 轴上的值的范围。</li><li><code>ylim</code> 用于指定 <code>y</code> 轴上的值的范围。</li><li><code>break</code> 用于提及每个条的宽度。</li></ul><h3 id="创建直方图"><a href="#创建直方图" class="headerlink" title="创建直方图"></a>创建直方图</h3><p>使用输入 <code>vector</code>，<code>label</code>，<code>col</code> 和边界参数创建一个简单的直方图。</p><p>下面给出的脚本将创建并保存当前 R 语言工作目录中的直方图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create data for the graph.</span></span><br><span class="line">v &lt;- <span class="built_in">c</span>(<span class="number">9</span>, <span class="number">13</span>, <span class="number">21</span>, <span class="number">8</span>, <span class="number">36</span>, <span class="number">22</span>, <span class="number">12</span>, <span class="number">41</span>, <span class="number">31</span>, <span class="number">33</span>, <span class="number">19</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;histogram.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the histogram.</span></span><br><span class="line">hist(v, xlab = <span class="string">&quot;Weight&quot;</span>, col = <span class="string">&quot;yellow&quot;</span>, border = <span class="string">&quot;blue&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/histogram.png" alt="直方图"></p><h3 id="X-和-Y-值的范围"><a href="#X-和-Y-值的范围" class="headerlink" title="X 和 Y 值的范围"></a>X 和 Y 值的范围</h3><p>要指定 X 轴和 Y 轴允许的值的范围，我们可以使用 <code>xlim</code> 和 <code>ylim</code> 参数。</p><p>每个条的宽度可以通过使用间隔来确定。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create data for the graph.</span></span><br><span class="line">v &lt;- <span class="built_in">c</span>(<span class="number">9</span>, <span class="number">13</span>, <span class="number">21</span>, <span class="number">8</span>, <span class="number">36</span>, <span class="number">22</span>, <span class="number">12</span>, <span class="number">41</span>, <span class="number">31</span>, <span class="number">33</span>, <span class="number">19</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;histogram_lim_breaks.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the histogram.</span></span><br><span class="line">hist(v, xlab = <span class="string">&quot;Weight&quot;</span>, col = <span class="string">&quot;green&quot;</span>, border = <span class="string">&quot;red&quot;</span>, xlim = <span class="built_in">c</span>(<span class="number">0</span>, <span class="number">40</span>), ylim = <span class="built_in">c</span>(<span class="number">0</span>, <span class="number">5</span>), breaks = <span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/histogram_lim_breaks.png" alt="直方图指定 X 和 Y 值的范围"></p><h2 id="折线图"><a href="#折线图" class="headerlink" title="折线图"></a>折线图</h2><p>折线图是通过在它们之间绘制线段来连接一系列点的图。这些点在它们的坐标（通常是 X 坐标）值之一中排序。折线图通常用于识别数据中的趋势。</p><p>R 语言中的 <code>plot()</code> 函数用于创建折线图。</p><h3 id="语法-3"><a href="#语法-3" class="headerlink" title="语法"></a>语法</h3><p>在 R 语言中创建折线图的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">plot(v, type, col, xlab, ylab)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>v</code> 是包含数值的向量。</li><li><code>type</code> 采用值 <code>p</code> 仅绘制点，<code>l</code> 仅绘制线和 <code>o</code> 绘制点和线。</li><li><code>xlab</code> 是 x 轴的标签。</li><li><code>ylab</code> 是 y 轴的标签。</li><li><code>main</code> 是图表的标题。</li><li><code>col</code> 用于给点和线的颜色。</li></ul><h3 id="创建折线图"><a href="#创建折线图" class="headerlink" title="创建折线图"></a>创建折线图</h3><p>使用输入向量和类型参数 <code>o</code> 创建简单的折线图。以下脚本将在当前 R 工作目录中创建并保存折线图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data for the chart.</span></span><br><span class="line">v &lt;- <span class="built_in">c</span>(<span class="number">7</span>, <span class="number">12</span>, <span class="number">28</span>, <span class="number">3</span>, <span class="number">41</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;line_chart.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the bar chart. </span></span><br><span class="line">plot(v, type = <span class="string">&quot;o&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/line_chart.png" alt="折线图"></p><h3 id="折线图标题，颜色和标签"><a href="#折线图标题，颜色和标签" class="headerlink" title="折线图标题，颜色和标签"></a>折线图标题，颜色和标签</h3><p>线图的特征可以通过使用附加参数来扩展。我们向点和线添加颜色，为图表添加标题，并向轴添加标签。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data for the chart.</span></span><br><span class="line">v &lt;- <span class="built_in">c</span>(<span class="number">7</span>, <span class="number">12</span>, <span class="number">28</span>, <span class="number">3</span>, <span class="number">41</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;line_chart_label_colored.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the bar chart.</span></span><br><span class="line">plot(v, type = <span class="string">&quot;o&quot;</span>, col = <span class="string">&quot;red&quot;</span>, xlab = <span class="string">&quot;Month&quot;</span>, ylab = <span class="string">&quot;Rain fall&quot;</span>, main = <span class="string">&quot;Rain fall chart&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/line_chart_label_colored.png" alt="折线图标题，颜色和标签"></p><h3 id="多线型折线图"><a href="#多线型折线图" class="headerlink" title="多线型折线图"></a>多线型折线图</h3><p>通过使用 <code>lines()</code> 函数，可以在同一个图表上绘制多条线。</p><p>在绘制第一行之后，<code>lines()</code> 函数可以使用一个额外的向量作为输入来绘制图表中的第二行。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data for the chart.</span></span><br><span class="line">v &lt;- <span class="built_in">c</span>(<span class="number">7</span>, <span class="number">12</span>, <span class="number">28</span>, <span class="number">3</span>, <span class="number">41</span>)</span><br><span class="line">t &lt;- <span class="built_in">c</span>(<span class="number">14</span>, <span class="number">7</span>, <span class="number">6</span>, <span class="number">19</span>, <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;line_chart_2_lines.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the bar chart.</span></span><br><span class="line">plot(v, type = <span class="string">&quot;o&quot;</span>, col = <span class="string">&quot;red&quot;</span>, xlab = <span class="string">&quot;Month&quot;</span>, ylab = <span class="string">&quot;Rain fall&quot;</span>, main = <span class="string">&quot;Rain fall chart&quot;</span>)</span><br><span class="line"></span><br><span class="line">lines(t, type = <span class="string">&quot;o&quot;</span>, col = <span class="string">&quot;blue&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/line_chart_2_lines.png" alt="多线型折线图"></p><h2 id="散点图"><a href="#散点图" class="headerlink" title="散点图"></a>散点图</h2><p>散点图显示在笛卡尔平面中绘制的许多点。每个点表示两个变量的值。在水平轴上选择一个变量，在垂直轴上选择另一个变量。</p><p>R 语言使用 <code>plot()</code> 函数创建简单散点图。</p><h3 id="语法-4"><a href="#语法-4" class="headerlink" title="语法"></a>语法</h3><p>在 R 语言中创建散点图的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">plot(x, y, main, xlab, ylab, xlim, ylim, axes)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是其值为水平坐标的数据集。</li><li><code>y</code> 是其值是垂直坐标的数据集。</li><li><code>main</code> 要是图形的图块。</li><li><code>xlab</code> 是水平轴上的标签。</li><li><code>ylab</code> 是垂直轴上的标签。</li><li><code>xlim</code> 是用于绘图的 <code>x</code> 的值的极限。</li><li><code>ylim</code> 是用于绘图的 <code>y</code> 的值的极限。</li><li><code>axes</code> 指示是否应在绘图上绘制两个轴。</li></ul><h3 id="创建散点图"><a href="#创建散点图" class="headerlink" title="创建散点图"></a>创建散点图</h3><p>我们使用 R 语言环境中可用的数据集 <code>mtcars</code> 来创建基本散点图。让我们使用 <code>mtcars</code> 中的 <code>wt</code> 和 <code>mpg</code> 列。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">input &lt;- mtcars[, <span class="built_in">c</span>(<span class="string">&#x27;wt&#x27;</span>, <span class="string">&#x27;mpg&#x27;</span>)]</span><br><span class="line">print(head(input))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">                    wt      mpg</span><br><span class="line">Mazda RX4           <span class="number">2.620</span>   <span class="number">21.0</span></span><br><span class="line">Mazda RX4 Wag       <span class="number">2.875</span>   <span class="number">21.0</span></span><br><span class="line">Datsun <span class="number">710</span>          <span class="number">2.320</span>   <span class="number">22.8</span></span><br><span class="line">Hornet <span class="number">4</span> Drive      <span class="number">3.215</span>   <span class="number">21.4</span></span><br><span class="line">Hornet Sportabout   <span class="number">3.440</span>   <span class="number">18.7</span></span><br><span class="line">Valiant             <span class="number">3.460</span>   <span class="number">18.1</span></span><br></pre></td></tr></table></figure></p><p>以下脚本将为 <code>wt</code>（重量）和 <code>mpg</code>（英里/加仑）之间的关系创建一个散点图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Get the input values.</span></span><br><span class="line">input &lt;- mtcars[, <span class="built_in">c</span>(<span class="string">&#x27;wt&#x27;</span>, <span class="string">&#x27;mpg&#x27;</span>)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;scatterplot.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the chart for cars with weight between 2.5 to 5 and mileage between 15 and 30.</span></span><br><span class="line">plot(x = input$wt, y = input$mpg, xlab = <span class="string">&quot;Weight&quot;</span>, ylab = <span class="string">&quot;Milage&quot;</span>, xlim = <span class="built_in">c</span>(<span class="number">2.5</span>, <span class="number">5</span>), ylim = <span class="built_in">c</span>(<span class="number">15</span>, <span class="number">30</span>), main = <span class="string">&quot;Weight vs Milage&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/scatterplot.png" alt="散点图"></p><h3 id="散点图矩阵"><a href="#散点图矩阵" class="headerlink" title="散点图矩阵"></a>散点图矩阵</h3><p>当我们有两个以上的变量，我们想找到一个变量和其余变量之间的相关性，我们使用散点图矩阵。我们使用 <code>pairs()</code> 函数创建散点图的矩阵。</p><h4 id="语法-5"><a href="#语法-5" class="headerlink" title="语法"></a>语法</h4><p>在 R 中创建散点图矩阵的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pairs(formula, data)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>formula</code> 表示成对使用的一系列变量。</li><li><code>data</code> 表示将从其获取变量的数据集。</li></ul><h4 id="创建散点图矩阵"><a href="#创建散点图矩阵" class="headerlink" title="创建散点图矩阵"></a>创建散点图矩阵</h4><p>每个变量与每个剩余变量配对。为每对绘制散点图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;scatterplot_matrices.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the matrices between 4 variables giving 12 plots.</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># One variable with 3 others and total 4 variables.</span></span><br><span class="line"></span><br><span class="line">pairs(~wt + mpg + disp + cyl, data = mtcars, main = <span class="string">&quot;Scatterplot Matrix&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当执行上面的代码中，我们得到以下输出。</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/scatterplot_matrices.png" alt="散点图矩阵"></p><h2 id="饼状图"><a href="#饼状图" class="headerlink" title="饼状图"></a>饼状图</h2><p>R 编程语言有许多库来创建图表和图表。饼图是将值表示为具有不同颜色的圆的切片。切片被标记，并且对应于每个片的数字也在图表中表示。</p><p>在 R 语言中，饼图是使用 <code>pie()</code> 函数创建的，它使用正数作为向量输入。附加参数用于控制标签，颜色，标题等。</p><h3 id="语法-6"><a href="#语法-6" class="headerlink" title="语法"></a>语法</h3><p>使用 R 语言创建饼图的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pie(x, labels, radius, main, col, clockwise)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是包含饼图中使用的数值的向量。</li><li><code>labels</code> 用于给出切片的描述。</li><li><code>radius</code> 表示饼图圆的半径（值 <code>-1</code> 和 <code>+1</code> 之间）。</li><li><code>main</code> 表示图表的标题。</li><li><code>col</code> 表示调色板。</li><li><code>clockwise</code> 是指示片段是顺时针还是逆时针绘制的逻辑值。</li></ul><h3 id="创建饼状图"><a href="#创建饼状图" class="headerlink" title="创建饼状图"></a>创建饼状图</h3><p>使用输入向量和标签创建一个非常简单的饼图。以下脚本将创建并保存当前 R 语言工作目录中的饼图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create data for the graph.</span></span><br><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">21</span>, <span class="number">62</span>, <span class="number">10</span>, <span class="number">53</span>)</span><br><span class="line">labels &lt;- <span class="built_in">c</span>(<span class="string">&quot;London&quot;</span>, <span class="string">&quot;New York&quot;</span>, <span class="string">&quot;Singapore&quot;</span>, <span class="string">&quot;Mumbai&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;pie.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the chart.</span></span><br><span class="line">pie(x, labels)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/pie.png" alt="饼状图"></p><h3 id="饼图标题和颜色"><a href="#饼图标题和颜色" class="headerlink" title="饼图标题和颜色"></a>饼图标题和颜色</h3><p>我们可以通过向函数中添加更多参数来扩展图表的功能。我们将使用参数 <code>main</code> 向图表添加标题，另一个参数是 <code>col</code>，它将在绘制图表时使用彩虹色板。托盘的长度应与图表中的值的数量相同。因此，我们使用 <code>length(x)</code>。</p><p>以下脚本将创建并保存当前 R 语言工作目录中的饼图。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create data for the graph.</span></span><br><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">21</span>, <span class="number">62</span>, <span class="number">10</span>, <span class="number">53</span>)</span><br><span class="line">labels &lt;- <span class="built_in">c</span>(<span class="string">&quot;London&quot;</span>, <span class="string">&quot;New York&quot;</span>, <span class="string">&quot;Singapore&quot;</span>, <span class="string">&quot;Mumbai&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;pie_title_colours.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the chart with title and rainbow color pallet.</span></span><br><span class="line">pie(x, labels, main = <span class="string">&quot;City pie chart&quot;</span>, col = rainbow(<span class="built_in">length</span>(x)))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/pie_title_colours.png" alt="饼图标题和颜色"></p><h3 id="切片百分比和图表图例"><a href="#切片百分比和图表图例" class="headerlink" title="切片百分比和图表图例"></a>切片百分比和图表图例</h3><p>我们可以通过创建其他图表变量来添加切片百分比和图表图例。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create data for the graph.</span></span><br><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">21</span>, <span class="number">62</span>, <span class="number">10</span>, <span class="number">53</span>)</span><br><span class="line">labels &lt;- <span class="built_in">c</span>(<span class="string">&quot;London&quot;</span>, <span class="string">&quot;New York&quot;</span>, <span class="string">&quot;Singapore&quot;</span>, <span class="string">&quot;Mumbai&quot;</span>)</span><br><span class="line"></span><br><span class="line">piepercent&lt;- <span class="built_in">round</span>(<span class="number">100</span>*x/<span class="built_in">sum</span>(x), <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;pie_percentage_legends.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the chart.</span></span><br><span class="line">pie(x, labels = piepercent, main = <span class="string">&quot;City pie chart&quot;</span>, col = rainbow(<span class="built_in">length</span>(x))), legend(<span class="string">&quot;topright&quot;</span>, <span class="built_in">c</span>(<span class="string">&quot;London&quot;</span>, <span class="string">&quot;New York&quot;</span>, <span class="string">&quot;Singapore&quot;</span>, <span class="string">&quot;Mumbai&quot;</span>), cex = <span class="number">0.8</span>, fill = rainbow(<span class="built_in">length</span>(x)))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/pie_percentage_legends.png" alt="饼图切片百分比和图表图例"></p><h3 id="3D-饼图"><a href="#3D-饼图" class="headerlink" title="3D 饼图"></a>3D 饼图</h3><p>可以使用其他软件包绘制具有 3 个维度的饼图。软件包 <code>plotrix</code> 有一个名为 <code>pie3D()</code> 的函数，用于此。</p><p>如果没有安装 <code>plotrix</code> 包可以使用 <code>install.packages(&quot;plotrix&quot;)</code> 语句安装。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Get the library.</span></span><br><span class="line">library(plotrix)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create data for the graph.</span></span><br><span class="line">x &lt;- <span class="built_in">c</span>(<span class="number">21</span>, <span class="number">62</span>, <span class="number">10</span>, <span class="number">53</span>)</span><br><span class="line">lbl &lt;- <span class="built_in">c</span>(<span class="string">&quot;London&quot;</span>, <span class="string">&quot;New York&quot;</span>, <span class="string">&quot;Singapore&quot;</span>, <span class="string">&quot;Mumbai&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give the chart file a name.</span></span><br><span class="line">png(file = <span class="string">&quot;3d_pie_chart.png&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot the chart.</span></span><br><span class="line">pie3D(x, labels = lbl, explode = <span class="number">0.1</span>, main = <span class="string">&quot;Pie Chart of Countries&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Save the file.</span></span><br><span class="line">dev.off()</span><br></pre></td></tr></table></figure></p><p>当我们执行上面的代码，它产生以下结果：</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_chart/3d_pie_chart.png" alt="3D 饼图"></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/R/">R</category>
      
      
      <comments>https://blog.eurkon.com/post/b35ac98a.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>R 语言教程</title>
      <link>https://blog.eurkon.com/post/de0e67e.html</link>
      <guid>https://blog.eurkon.com/post/de0e67e.html</guid>
      <pubDate>Mon, 18 Jan 2021 08:20:11 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h2&gt;&lt;h3 id=&quot;R-的简介&quot;&gt;&lt;a href=&quot;#R-的简介&quot; class=&quot;headerlink&quot; title=&quot;R 的简介&quot;&gt;&lt;/a&gt;R 的简</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><h3 id="R-的简介"><a href="#R-的简介" class="headerlink" title="R 的简介"></a>R 的简介</h3><ul><li>R 语言是用于统计分析，图形表示和报告的编程语言和软件环境。R 语言由 Ross Ihaka 和 Robert Gentleman 在新西兰奥克兰大学创建，目前由 R 语言开发核心团队开发。</li><li>R 语言的核心是解释计算机语言，其允许分支和循环以及使用函数的模块化编程。R 语言允许与以 C，C ++，.Net，Python 或 FORTRAN 语言编写的过程集成以提高效率。</li><li>R 语言在 GNU 通用公共许可证下免费提供，并为各种操作系统（如 Linux，Windows 和 Mac）提供预编译的二进制版本。</li><li>R 是一个在 GNU 风格的副本左侧的自由软件，GNU 项目的官方部分叫做 GNU S.</li></ul><h3 id="R-的演变"><a href="#R-的演变" class="headerlink" title="R 的演变"></a>R 的演变</h3><p>R 语言最初是由新西兰奥克兰奥克兰大学统计系的 Ross Ihaka 和 Robert Gentleman 写的。R 语言于 1993 年首次亮相。</p><ul><li>一大群人通过发送代码和错误报告对 R 做出了贡献。</li><li>自 1997 年年中以来，已经有一个核心组（R 核心团队）可以修改 R 源代码归档。</li></ul><h3 id="R-的特点"><a href="#R-的特点" class="headerlink" title="R 的特点"></a>R 的特点</h3><p>如前所述，R 语言是用于统计分析，图形表示和报告的编程语言和软件环境。以下是 R 语言的重要特点：</p><ul><li>R 语言是一种开发良好，简单有效的编程语言，包括条件，循环，用户定义的递归函数以及输入和输出设施。</li><li>R 语言具有有效的数据处理和存储设施，</li><li>R 语言提供了一套用于数组，列表，向量和矩阵计算的运算符。</li><li>R 语言为数据分析提供了大型，一致和集成的工具集合。</li><li>R 语言提供直接在计算机上或在纸张上打印的图形设施用于数据分析和显示。</li></ul><p>R 语言是世界上最广泛使用的统计编程语言。它是数据科学家的第一选择，并由一个充满活力和有才华的贡献者社区支持。</p><h2 id="基本语法"><a href="#基本语法" class="headerlink" title="基本语法"></a>基本语法</h2><p>根据需要，您可以在 R 语言命令提示符处编程，也可以使用 R 语言脚本文件编写程序。</p><h3 id="命令提示符"><a href="#命令提示符" class="headerlink" title="命令提示符"></a>命令提示符</h3><p>如果你已经配置好 R 语言环境，那么你只需要按一下的命令便可轻易开启命令提示符</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> R</span></span><br></pre></td></tr></table></figure></p><p>这将启动 R 语言解释器，你会得到一个提示 <code>&gt;</code> 在那里你可以开始输入你的程序，具体如下。</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;</span><span class="bash"> myString &lt;- <span class="string">&quot;Hello, World!&quot;</span></span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> <span class="built_in">print</span>(myString)</span></span><br><span class="line">[1] &quot;Hello, World!&quot;</span><br></pre></td></tr></table></figure></p><p>在这里，第一个语句先定义一个字符串变量 <code>myString</code>，并将 <code>Hello，World！</code> 赋值其中，第二句则使用 <code>print()</code> 语句将变量 <code>myString</code> 的内容进行打印。</p><h3 id="脚本文件"><a href="#脚本文件" class="headerlink" title="脚本文件"></a>脚本文件</h3><p>通常，您将通过在脚本文件中编写程序来执行编程，然后在命令提示符下使用 R 解释器（称为 <code>Rscript</code>）来执行这些脚本。所以让我们开始在一个命名为 <code>test.R</code> 的文本文件中编写下面的代码</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> My first program <span class="keyword">in</span> R Programming</span></span><br><span class="line">myString &lt;- &quot;Hello, World!&quot;</span><br><span class="line">print(myString)</span><br></pre></td></tr></table></figure></p><p>将上述代码保存在 <code>test.R</code> 文件中，并在 Linux 命令提示符下执行，如下所示。即使您使用的是 Windows 或其他系统，语法也将保持不变。</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> Rscript test.R</span> </span><br></pre></td></tr></table></figure></p><p>当我们运行上面的程序，它产生以下结果：<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[1] &quot;Hello, World!&quot;</span><br></pre></td></tr></table></figure></p><h3 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h3><p>注释能帮助您解释 R 语言程序中的脚本，它们在实际执行程序时会被解释器忽略。单个注释使用 <code>#</code> 在语句的开头写入，如下所示</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># My first program in R Programming</span></span><br></pre></td></tr></table></figure></p><p>R 语言不支持多行注释，但你可以使用一个小技巧，如下</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="literal">FALSE</span>) &#123;</span><br><span class="line">   <span class="string">&quot;This is a demo for multi-line comments and it should be put inside either a single</span></span><br><span class="line"><span class="string">      OR double quote&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">myString &lt;- <span class="string">&quot;Hello, World!&quot;</span></span><br><span class="line">print(myString)</span><br></pre></td></tr></table></figure></p><p>虽然上面的注释将由 R 解释器执行，但它们不会干扰您的实际程序。但是你必须为内容加上单引号或双引号。</p><h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><p>通常，在使用任何编程语言进行编程时，您需要使用各种变量来存储各种信息。变量只是保留值的存储位置。这意味着，当你创建一个变量，你必须在内存中保留一些空间来存储它们。</p><p>您可能想存储各种数据类型的信息，如字符，宽字符，整数，浮点，双浮点，布尔等。基于变量的数据类型，操作系统分配内存并决定什么可以存储在保留内存中。</p><p>与其他编程语言（如 C 中的 C 和 Java）相反，变量不会声明为某种数据类型。变量分配有 R 对象，R 对象的数据类型变为变量的数据类型。尽管有很多类型的 R 对象，但经常使用的是：</p><ul><li><a href="#Vectors-向量">Vectors 向量</a></li><li><a href="#Lists-列表">Lists 列表</a></li><li><a href="#Matrices-矩阵">Matrices 矩阵</a></li><li><a href="#Arrays-数组">Arrays 数组</a></li><li><a href="#Factors-因子">Factors 因子</a></li><li><a href="#Data-Frames-数据框">Data Frames 数据框</a></li></ul><p>这些对象中最简单的是向量对象，并且这些原子向量有六种数据类型，也称为六类向量。其他 R 对象建立在原子向量之上。</p><div class="table-container"><table><thead><tr><th style="text-align:center">数据类型</th><th style="text-align:center">取值</th><th style="text-align:left">实例</th></tr></thead><tbody><tr><td style="text-align:center">Logical（逻辑型）</td><td style="text-align:center">TRUE, FALSE</td><td style="text-align:left"><code>v &lt;- TRUE</code> <br> <code>print(class(v))</code> <br> <code>[1] &quot;logical&quot;</code></td></tr><tr><td style="text-align:center">Numeric（数字）</td><td style="text-align:center">12.3，5，999</td><td style="text-align:left"><code>v &lt;- 23.5</code> <br> <code>print(class(v))</code> <br> <code>[1] &quot;numeric&quot;</code></td></tr><tr><td style="text-align:center">Integer（整型）</td><td style="text-align:center">2L，34L，0L</td><td style="text-align:left"><code>v &lt;- 2L</code> <br> <code>print(class(v))</code> <br> <code>[1] &quot;integer&quot;</code></td></tr><tr><td style="text-align:center">Complex（复合型）</td><td style="text-align:center">3 + 2i</td><td style="text-align:left"><code>v &lt;- 2+5i</code> <br> <code>print(class(v))</code> <br> <code>[1] &quot;complex&quot;</code></td></tr><tr><td style="text-align:center">Character（字符）</td><td style="text-align:center">&#39;a&#39; , &quot;good&quot;, &quot;TRUE&quot;, &#39;23.4&#39;</td><td style="text-align:left"><code>v &lt;- &quot;TRUE&quot;</code> <br> <code>print(class(v))</code> <br> <code>[1] &quot;character&quot;</code></td></tr><tr><td style="text-align:center">Raw（原型）</td><td style="text-align:center">&quot;Hello&quot; 被存储为 48 65 6c 6c 6f</td><td style="text-align:left"><code>v &lt;- charToRaw(&quot;Hello&quot;)</code> <br> <code>print(class(v))</code> <br> <code>[1] &quot;raw&quot;</code></td></tr></tbody></table></div><p>在 R 编程中，非常基本的数据类型是称为向量的 R 对象，其保存如上所示的不同类的元素。请注意，在 R 中，类的数量不仅限于上述六种类型。例如，我们可以使用许多原子向量并创建一个数组，其类将成为数组。</p><h3 id="Vectors-向量"><a href="#Vectors-向量" class="headerlink" title="Vectors 向量"></a>Vectors 向量</h3><p>当你想用多个元素创建向量时，你应该使用 <code>c()</code> 函数，这意味着将元素组合成一个向量。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a vector.</span></span><br><span class="line">apple &lt;- <span class="built_in">c</span>(<span class="string">&quot;red&quot;</span>, <span class="string">&quot;green&quot;</span>, <span class="string">&quot;yellow&quot;</span>)</span><br><span class="line">print(apple)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the class of the vector.</span></span><br><span class="line">print(<span class="built_in">class</span>(apple))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;red&quot;</span>    <span class="string">&quot;green&quot;</span>  <span class="string">&quot;yellow&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;character&quot;</span></span><br></pre></td></tr></table></figure></p><p>更多方法实例请点击查看 R 语言<a href="#向量">向量</a>章节。</p><h3 id="Lists-列表"><a href="#Lists-列表" class="headerlink" title="Lists 列表"></a>Lists 列表</h3><p>列表是一个 R 对象，它可以在其中包含许多不同类型的元素，如向量，函数甚至其中的另一个列表。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a list.</span></span><br><span class="line">list1 &lt;- <span class="built_in">list</span>(<span class="built_in">c</span>(<span class="number">2</span>, <span class="number">5</span>, <span class="number">3</span>), <span class="number">21.3</span>, <span class="built_in">sin</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the list.</span></span><br><span class="line">print(list1)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">1</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="number">2</span> <span class="number">5</span> <span class="number">3</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">2</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="number">21.3</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">3</span>]]</span><br><span class="line"><span class="keyword">function</span> (x)  .Primitive(<span class="string">&quot;sin&quot;</span>)</span><br></pre></td></tr></table></figure></p><p>更多方法实例请点击查看 R 语言<a href="#列表">列表</a>章节。</p><h3 id="Matrices-矩阵"><a href="#Matrices-矩阵" class="headerlink" title="Matrices 矩阵"></a>Matrices 矩阵</h3><p>矩阵是二维矩形数据集。它可以使用矩阵函数的向量输入创建。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a matrix.</span></span><br><span class="line">M = matrix( <span class="built_in">c</span>(<span class="string">&#x27;a&#x27;</span>, <span class="string">&#x27;a&#x27;</span>, <span class="string">&#x27;b&#x27;</span>, <span class="string">&#x27;c&#x27;</span>, <span class="string">&#x27;b&#x27;</span>, <span class="string">&#x27;a&#x27;</span>), nrow = <span class="number">2</span>, ncol = <span class="number">3</span>, byrow = <span class="literal">TRUE</span>)</span><br><span class="line">print(M)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,] <span class="string">&quot;a&quot;</span>  <span class="string">&quot;a&quot;</span>  <span class="string">&quot;b&quot;</span> </span><br><span class="line">[<span class="number">2</span>,] <span class="string">&quot;c&quot;</span>  <span class="string">&quot;b&quot;</span>  <span class="string">&quot;a&quot;</span></span><br></pre></td></tr></table></figure></p><p>更多方法实例请点击查看 R 语言<a href="#矩阵">矩阵</a>章节。</p><h3 id="Arrays-数组"><a href="#Arrays-数组" class="headerlink" title="Arrays 数组"></a>Arrays 数组</h3><p>虽然矩阵被限制为二维，但阵列可以具有任何数量的维度。数组函数使用一个 <code>dim</code> 属性创建所需的维数。在下面的例子中，我们创建了一个包含两个元素的数组，每个元素为 <code>3x3</code> 个矩阵。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create an array.</span></span><br><span class="line">a &lt;- array(<span class="built_in">c</span>(<span class="string">&#x27;green&#x27;</span>, <span class="string">&#x27;yellow&#x27;</span>), <span class="built_in">dim</span> = <span class="built_in">c</span>(<span class="number">3</span>, <span class="number">3</span>, <span class="number">2</span>))</span><br><span class="line">print(a)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">, , <span class="number">1</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>]     [,<span class="number">2</span>]     [,<span class="number">3</span>]    </span><br><span class="line">[<span class="number">1</span>,] <span class="string">&quot;green&quot;</span>  <span class="string">&quot;yellow&quot;</span> <span class="string">&quot;green&quot;</span> </span><br><span class="line">[<span class="number">2</span>,] <span class="string">&quot;yellow&quot;</span> <span class="string">&quot;green&quot;</span>  <span class="string">&quot;yellow&quot;</span></span><br><span class="line">[<span class="number">3</span>,] <span class="string">&quot;green&quot;</span>  <span class="string">&quot;yellow&quot;</span> <span class="string">&quot;green&quot;</span> </span><br><span class="line"></span><br><span class="line">, , <span class="number">2</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>]     [,<span class="number">2</span>]     [,<span class="number">3</span>]    </span><br><span class="line">[<span class="number">1</span>,] <span class="string">&quot;yellow&quot;</span> <span class="string">&quot;green&quot;</span>  <span class="string">&quot;yellow&quot;</span></span><br><span class="line">[<span class="number">2</span>,] <span class="string">&quot;green&quot;</span>  <span class="string">&quot;yellow&quot;</span> <span class="string">&quot;green&quot;</span> </span><br><span class="line">[<span class="number">3</span>,] <span class="string">&quot;yellow&quot;</span> <span class="string">&quot;green&quot;</span>  <span class="string">&quot;yellow&quot;</span></span><br></pre></td></tr></table></figure></p><p>更多方法实例请点击查看 R 语言<a href="#数组">数组</a>章节。</p><h3 id="Factors-因子"><a href="#Factors-因子" class="headerlink" title="Factors 因子"></a>Factors 因子</h3><p>因子是使用向量创建的 R 对象。它将向量与向量中元素的不同值一起存储为标签。标签总是字符，不管它在输入向量中是数字还是字符或布尔等。它们在统计建模中非常有用。使用 <code>factor()</code> 函数创建因子。<code>nlevels</code> 函数给出级别计数。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a vector.</span></span><br><span class="line">apple_colors &lt;- <span class="built_in">c</span>(<span class="string">&#x27;green&#x27;</span>, <span class="string">&#x27;green&#x27;</span>, <span class="string">&#x27;yellow&#x27;</span>, <span class="string">&#x27;red&#x27;</span>, <span class="string">&#x27;red&#x27;</span>, <span class="string">&#x27;red&#x27;</span>, <span class="string">&#x27;green&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create a factor object.</span></span><br><span class="line">factor_apple &lt;- factor(apple_colors)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the factor.</span></span><br><span class="line">print(factor_apple)</span><br><span class="line">print(nlevels(factor_apple))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] green  green  yellow  red   red   red   green </span><br><span class="line">Levels: green red yellow</span><br><span class="line"><span class="comment"># applying the nlevels function we can know the number of distinct values</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">3</span></span><br></pre></td></tr></table></figure></p><p>更多方法实例请点击查看 R 语言<a href="#因子">因子</a>章节。</p><h3 id="Data-Frames-数据框"><a href="#Data-Frames-数据框" class="headerlink" title="Data Frames 数据框"></a>Data Frames 数据框</h3><p>数据框是表格数据对象。与数据框中的矩阵不同，每列可以包含不同的数据模式。第一列可以是数字，而第二列可以是字符，第三列可以是逻辑的。它是等长度的向量的列表。使用 <code>data.frame()</code> 函数创建数据框。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data frame.</span></span><br><span class="line">BMI &lt;-  | data.frame(</span><br><span class="line">   gender = <span class="built_in">c</span>(<span class="string">&quot;Male&quot;</span>, <span class="string">&quot;Male&quot;</span>, <span class="string">&quot;Female&quot;</span>),</span><br><span class="line">   height = <span class="built_in">c</span>(<span class="number">152</span>, <span class="number">171.5</span>, <span class="number">165</span>),</span><br><span class="line">   weight = <span class="built_in">c</span>(<span class="number">81</span>, <span class="number">93</span>, <span class="number">78</span>),</span><br><span class="line">   Age = <span class="built_in">c</span>(<span class="number">42</span>, <span class="number">38</span>, <span class="number">26</span>)</span><br><span class="line">)</span><br><span class="line">print(BMI)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  gender height weight Age</span><br><span class="line">1   Male  <span class="number">152.0</span>     <span class="number">81</span>  <span class="number">42</span></span><br><span class="line">2   Male  <span class="number">171.5</span>     <span class="number">93</span>  <span class="number">38</span></span><br><span class="line">3 Female  <span class="number">165.0</span>     <span class="number">78</span>  <span class="number">26</span></span><br></pre></td></tr></table></figure></p><p>更多方法实例请点击查看 R 语言<a href="#数据框">数据框</a>章节。</p><h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><p>变量为我们提供了我们的程序可以操作的命名存储。R 语言中的变量可以存储原子向量，原子向量组或许多 <code>Robject</code> 的组合。有效的变量名称由字母，数字和点或下划线字符组成。变量名以字母或不以数字后跟的点开头。</p><div class="table-container"><table><thead><tr><th style="text-align:center">变量名</th><th style="text-align:center">合法性</th><th style="text-align:center">原因</th></tr></thead><tbody><tr><td style="text-align:center">var_name2.</td><td style="text-align:center">有效</td><td style="text-align:center">有字母，数字，点和下划线</td></tr><tr><td style="text-align:center">VAR_NAME％</td><td style="text-align:center">无效</td><td style="text-align:center">有字符&#39;％&#39;。只有点(.)和下划线允许的。</td></tr><tr><td style="text-align:center">2var_name</td><td style="text-align:center">无效</td><td style="text-align:center">以数字开头</td></tr><tr><td style="text-align:center">.var_name, var.name</td><td style="text-align:center">有效</td><td style="text-align:center">可以用一个点(.)，但启动点(.)，不应该后跟一个数字。</td></tr><tr><td style="text-align:center">.2var_name</td><td style="text-align:center">无效</td><td style="text-align:center">起始点后面是数字使其无效。</td></tr><tr><td style="text-align:center">_var_name</td><td style="text-align:center">无效</td><td style="text-align:center">开头_这是无效的</td></tr></tbody></table></div><p>可以使用向左，向右和等于运算符来为变量分配值。可以使用 <code>print()</code> 或 <code>cat()</code> 函数打印变量的值。<code>cat()</code> 函数将多个项目组合成连续打印输出。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Assignment using equal operator.</span></span><br><span class="line">var.1 = <span class="built_in">c</span>(<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Assignment using leftward operator.</span></span><br><span class="line">var.2 &lt;- <span class="built_in">c</span>(<span class="string">&quot;learn&quot;</span>, <span class="string">&quot;R&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Assignment using rightward operator.</span></span><br><span class="line">c(TRUE, 1) -&gt; var.3</span><br><span class="line"></span><br><span class="line">print(var.1)</span><br><span class="line">cat (<span class="string">&quot;var.1 is &quot;</span>, var.1 , <span class="string">&quot;&quot;</span>)</span><br><span class="line">cat (<span class="string">&quot;var.2 is &quot;</span>, var.2 , <span class="string">&quot;&quot;</span>)</span><br><span class="line">cat (<span class="string">&quot;var.3 is &quot;</span>, var.3 , <span class="string">&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">0</span> <span class="number">1</span> <span class="number">2</span> <span class="number">3</span></span><br><span class="line">var.1 is  <span class="number">0</span> <span class="number">1</span> <span class="number">2</span> <span class="number">3</span> </span><br><span class="line">var.2 is  learn R </span><br><span class="line">var.3 is  <span class="number">1</span> <span class="number">1</span> </span><br></pre></td></tr></table></figure></p><p>变量的数据类型在 R 语言中，变量本身没有声明任何数据类型，而是获取分配给它的 R 对象的数据类型。所以 R 称为动态类型语言，这意味着我们可以在程序中使用同一个变量时，一次又一次地更改变量的数据类型。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">var_x &lt;- <span class="string">&quot;Hello&quot;</span></span><br><span class="line">cat(<span class="string">&quot;The class of var_x is &quot;</span>, <span class="built_in">class</span>(var_x), <span class="string">&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line">var_x &lt;- 34.5</span><br><span class="line">cat(<span class="string">&quot;  Now the class of var_x is &quot;</span>, <span class="built_in">class</span>(var_x), <span class="string">&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line">var_x &lt;- 27L</span><br><span class="line">cat(<span class="string">&quot;   Next the class of var_x becomes &quot;</span>, <span class="built_in">class</span>(var_x), <span class="string">&quot;&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">The <span class="built_in">class</span> of var_x is  character </span><br><span class="line">   Now the <span class="built_in">class</span> of var_x is  numeric </span><br><span class="line">      Next the <span class="built_in">class</span> of var_x becomes  integer</span><br></pre></td></tr></table></figure></p><h3 id="查找变量"><a href="#查找变量" class="headerlink" title="查找变量"></a>查找变量</h3><p>要知道工作空间中当前可用的所有变量，我们使用 <code>ls()</code> 函数。<code>ls()</code> 函数也可以使用模式来匹配变量名。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">print(ls())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;my var&quot;</span>     <span class="string">&quot;my_new_var&quot;</span> <span class="string">&quot;my_var&quot;</span>     <span class="string">&quot;var.1&quot;</span>      </span><br><span class="line">[<span class="number">5</span>] <span class="string">&quot;var.2&quot;</span>      <span class="string">&quot;var.3&quot;</span>      <span class="string">&quot;var.name&quot;</span>   <span class="string">&quot;var_name2.&quot;</span></span><br><span class="line">[<span class="number">9</span>] <span class="string">&quot;var_x&quot;</span>      <span class="string">&quot;varname&quot;</span> </span><br></pre></td></tr></table></figure></p><p><strong>注意</strong> ：它是一个示例输出，取决于在您的环境中声明的变量。</p><p><code>ls()</code> 函数可以使用模式来匹配变量名。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># List the variables starting with the pattern &quot;var&quot;.</span></span><br><span class="line">print(ls(pattern = <span class="string">&quot;var&quot;</span>))   </span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;my var&quot;</span>     <span class="string">&quot;my_new_var&quot;</span> <span class="string">&quot;my_var&quot;</span>     <span class="string">&quot;var.1&quot;</span>      </span><br><span class="line">[<span class="number">5</span>] <span class="string">&quot;var.2&quot;</span>      <span class="string">&quot;var.3&quot;</span>      <span class="string">&quot;var.name&quot;</span>   <span class="string">&quot;var_name2.&quot;</span></span><br><span class="line">[<span class="number">9</span>] <span class="string">&quot;var_x&quot;</span>      <span class="string">&quot;varname&quot;</span>    </span><br></pre></td></tr></table></figure></p><p>以点 <code>.</code> 开头的变量被隐藏，它们可以使用 <code>ls()</code> 函数的 <code>all.names = TRUE</code> 参数列出。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">print(ls(all.name = <span class="literal">TRUE</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;.cars&quot;</span>        <span class="string">&quot;.Random.seed&quot;</span> <span class="string">&quot;.var_name&quot;</span>    <span class="string">&quot;.varname&quot;</span>     <span class="string">&quot;.varname2&quot;</span>   </span><br><span class="line">[<span class="number">6</span>] <span class="string">&quot;my var&quot;</span>       <span class="string">&quot;my_new_var&quot;</span>   <span class="string">&quot;my_var&quot;</span>       <span class="string">&quot;var.1&quot;</span>        <span class="string">&quot;var.2&quot;</span>        </span><br><span class="line">[<span class="number">11</span>]<span class="string">&quot;var.3&quot;</span>        <span class="string">&quot;var.name&quot;</span>     <span class="string">&quot;var_name2.&quot;</span>   <span class="string">&quot;var_x&quot;</span>  </span><br></pre></td></tr></table></figure></p><h3 id="删除变量"><a href="#删除变量" class="headerlink" title="删除变量"></a>删除变量</h3><p>可以使用 <code>rm()</code> 函数删除变量。下面我们删除变量 <code>var.3</code>。打印时，抛出变量错误的值。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">rm(var.3)</span><br><span class="line">print(var.3)</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;var.3&quot;</span></span><br><span class="line">Error <span class="keyword">in</span> print(var.3) : object <span class="string">&#x27;var.3&#x27;</span> not found</span><br></pre></td></tr></table></figure></p><p>所有的变量可以通过使用 <code>rm()</code> 和 <code>ls()</code> 函数一起删除。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">rm(<span class="built_in">list</span> = ls())</span><br><span class="line">print(ls())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">character(<span class="number">0</span>)</span><br></pre></td></tr></table></figure></p><h2 id="运算符"><a href="#运算符" class="headerlink" title="运算符"></a>运算符</h2><p>运算符是一个符号，通知编译器执行特定的数学或逻辑操作。R 语言具有丰富的内置运算符，并提供以下类型的运算符。</p><h3 id="运算符的类型"><a href="#运算符的类型" class="headerlink" title="运算符的类型"></a>运算符的类型</h3><p>R 语言中拥有如下几种运算符类型：</p><ul><li><a href="#算术运算符">算术运算符</a></li><li><a href="#关系运算符">关系运算符</a></li><li><a href="#逻辑运算符">逻辑运算符</a></li><li><a href="#赋值运算符">赋值运算符</a></li><li><a href="#其他运算符">其他运算符</a></li></ul><h3 id="算术运算符"><a href="#算术运算符" class="headerlink" title="算术运算符"></a>算术运算符</h3><p>下表显示了 R 语言支持的算术运算符。操作符对向量的每个元素起作用。</p><div class="table-container"><table><thead><tr><th style="text-align:center">运算符</th><th style="text-align:center">描述</th><th style="text-align:left">实例</th></tr></thead><tbody><tr><td style="text-align:center">+</td><td style="text-align:center">两个向量相加</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6)</code> <br> <code>t &lt;- c(8, 3, 4)</code> <br> <code>print(v+t)</code> <br> 它产生以下结果： <br> <code>[1] 10.0  8.5  10.0</code></td></tr><tr><td style="text-align:center">-</td><td style="text-align:center">两个向量相减</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6)</code> <br> <code>t &lt;- c(8, 3, 4)</code> <br> <code>print(v-t)</code> <br> 它产生以下结果： <br> <code>[1] -6.0  2.5  2.0</code></td></tr><tr><td style="text-align:center">*</td><td style="text-align:center">两个向量相乘</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6)</code> <br> <code>t &lt;- c(8, 3, 4)</code> <br> <code>print(v*t)</code> <br> 它产生以下结果： <br> <code>[1] 16.0 16.5 24.0</code></td></tr><tr><td style="text-align:center">/</td><td style="text-align:center">将第一个向量与第二个向量相除</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6)</code> <br> <code>t &lt;- c(8, 3, 4)</code> <br> <code>print(v/t)</code> <br> 它产生以下结果： <br> <code>[1] 0.250000 1.833333 1.500000</code></td></tr><tr><td style="text-align:center">%%</td><td style="text-align:center">两个向量求余</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6)</code> <br> <code>t &lt;- c(8, 3, 4)</code> <br> <code>print(v%%t)</code> <br> 它产生以下结果： <br> <code>[1] 2.0 2.5 2.0</code></td></tr><tr><td style="text-align:center">%/%</td><td style="text-align:center">两个向量相除求商</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6)</code> <br> <code>t &lt;- c(8, 3, 4)</code> <br> <code>print(v%/%t)</code> <br> 它产生以下结果： <br> <code>[1] 0 1 1</code></td></tr><tr><td style="text-align:center">^</td><td style="text-align:center">将第二向量作为第一向量的指数</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6)</code> <br> <code>t &lt;- c(8, 3, 4)</code> <br> <code>print(v^t)</code> <br> 它产生以下结果： <br> <code>[1]  256.000  166.375 1296.000</code></td></tr></tbody></table></div><h3 id="关系运算符"><a href="#关系运算符" class="headerlink" title="关系运算符"></a>关系运算符</h3><p>下表显示了 R 语言支持的关系运算符。将第一向量的每个元素与第二向量的相应元素进行比较。比较的结果是布尔值。</p><div class="table-container"><table><thead><tr><th style="text-align:center">运算符</th><th style="text-align:center">描述</th><th style="text-align:left">实例</th></tr></thead><tbody><tr><td style="text-align:center">&gt;</td><td style="text-align:center">检查第一向量的每个元素是否大于第二向量的相应元素。</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6, 9)</code> <br> <code>t &lt;- c(8, 2.5, 14, 9)</code> <br> <code>print(v&gt;t)</code> <br> 它产生以下结果： <br> <code>[1] FALSE TRUE FALSE FALSE</code></td></tr><tr><td style="text-align:center">&lt;</td><td style="text-align:center">检查第一个向量的每个元素是否小于第二个向量的相应元素。</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6, 9)</code> <br> <code>t &lt;- c(8, 2.5, 14, 9)</code> <br> <code>print(v &lt; t)</code> <br> 它产生以下结果： <br> <code>[1] TRUE FALSE TRUE FALSE</code></td></tr><tr><td style="text-align:center">==</td><td style="text-align:center">检查第一个向量的每个元素是否等于第二个向量的相应元素。</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6, 9)</code> <br> <code>t &lt;- c(8, 2.5, 14, 9)</code> <br> <code>print(v == t)</code> <br> 它产生以下结果： <br> <code>[1] FALSE FALSE FALSE TRUE</code></td></tr><tr><td style="text-align:center">&lt;=</td><td style="text-align:center">检查第一向量的每个元素是否小于或等于第二向量的相应元素。</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6, 9)</code> <br> <code>t &lt;- c(8, 2.5, 14, 9)</code> <br> <code>print(v&lt;=t)</code> <br> 它产生以下结果： <br> <code>[1] TRUE FALSE TRUE TRUE</code></td></tr><tr><td style="text-align:center">&gt;=</td><td style="text-align:center">检查第一向量的每个元素是否大于或等于第二向量的相应元素。</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6, 9)</code> <br> <code>t &lt;- c(8, 2.5, 14, 9)</code> <br> <code>print(v&gt;=t)</code> <br> 它产生以下结果： <br> <code>[1] FALSE  TRUE FALSE  TRUE</code></td></tr><tr><td style="text-align:center">!=</td><td style="text-align:center">检查第一个向量的每个元素是否不等于第二个向量的相应元素。</td><td style="text-align:left"><code>v &lt;- c(2, 5.5, 6, 9</code> <br> <code>t &lt;- c(8, 2.5, 14, 9)</code> <br> <code>print(v!=t)</code> <br> 它产生以下结果： <br> <code>[1] TRUE TRUE TRUE FALSE</code></td></tr></tbody></table></div><h3 id="逻辑运算符"><a href="#逻辑运算符" class="headerlink" title="逻辑运算符"></a>逻辑运算符</h3><p>下表显示了 R 语言支持的逻辑运算符。它只适用于逻辑，数字或复杂类型的向量。所有大于 1 的数字被认为是逻辑值 <code>TRUE</code>。</p><p>将第一向量的每个元素与第二向量的相应元素进行比较。比较的结果是布尔值。</p><div class="table-container"><table><thead><tr><th style="text-align:center">运算符</th><th style="text-align:center">描述</th><th style="text-align:left">实例</th></tr></thead><tbody><tr><td style="text-align:center">&amp;</td><td style="text-align:center">它被称为元素逻辑 AND 运算符。<br> 它将第一向量的每个元素与第二向量的相应元素组合， <br> 并且如果两个元素都为 TRUE，则给出输出 TRUE。</td><td style="text-align:left"><code>v &lt;- c(3, 1, TRUE, 2+3i)</code> <br> <code>t &lt;- c(4, 1, FALSE, 2+3i)</code> <br> <code>print(v&amp;t)</code> <br> 它产生以下结果： <br> <code>[1] TRUE TRUE FALSE TRUE</code></td></tr><tr><td style="text-align:center">&amp;vert；</td><td style="text-align:center">它被称为元素逻辑或运算符。<br> 它将第一向量的每个元素与第二向量的相应元素组合， <br> 并且如果元素为真，则给出输出 TRUE。</td><td style="text-align:left"><code>v &lt;- c(3, 0, TRUE, 2+2i)</code> <br> <code>t &lt;- c(4, 0, FALSE, 2+3i)</code> <br> <code>print(v</code>&vert;<code>t)</code> <br> 它产生以下结果： <br> <code>[1] TRUE FALSE TRUE TRUE</code></td></tr><tr><td style="text-align:center">!</td><td style="text-align:center">它被称为逻辑非运算符。取得向量的每个元素，并给出相反的逻辑值。</td><td style="text-align:left"><code>v &lt;- c(3, 0, TRUE, 2+2i)</code> <br> <code>print(!v)</code> <br> 它产生以下结果： <br> <code>[1] FALSE TRUE FALSE FALSE</code></td></tr></tbody></table></div><p>逻辑运算符 <code>&amp;&amp;</code> 和 <code>||</code> 只考虑向量的第一个元素，给出单个元素的向量作为输出。</p><div class="table-container"><table><thead><tr><th style="text-align:center">运算符</th><th style="text-align:center">描述</th><th style="text-align:left">实例</th></tr></thead><tbody><tr><td style="text-align:center">&amp;&amp;</td><td style="text-align:center">称为逻辑 AND 运算符。取两个向量的第一个元素， <br> 并且只有两个都为 TRUE 时才给出 TRUE。</td><td style="text-align:left"><code>v &lt;- c(3, 0, TRUE, 2+2i)</code> <br> <code>t &lt;- c(1, 3, TRUE, 2+3i)</code> <br> <code>print(v&amp;&amp;t)</code> <br> 它产生以下结果： <br> <code>[1] TRUE</code></td></tr><tr><td style="text-align:center">&vert;&amp;vert；</td><td style="text-align:center">称为逻辑 OR 运算符。取两个向量的第一个元素， <br> 如果其中一个为 TRUE，则给出 TRUE。</td><td style="text-align:left"><code>v &lt;- c(0, 0, TRUE, 2+2i)</code> <br> <code>t &lt;- c(0, 3, TRUE, 2+3i)</code> <br> <code>print(v</code>&vert;&vert;<code>t)</code> <br> 它产生以下结果： <br> <code>[1] FALSE</code></td></tr></tbody></table></div><h3 id="赋值运算符"><a href="#赋值运算符" class="headerlink" title="赋值运算符"></a>赋值运算符</h3><p>这些运算符用于向向量赋值。</p><div class="table-container"><table><thead><tr><th style="text-align:center">运算符</th><th style="text-align:center">描述</th><th style="text-align:left">实例</th></tr></thead><tbody><tr><td style="text-align:center">&lt;− <br> = <br> &lt;&lt;− <br></td><td style="text-align:center">称为左分配</td><td style="text-align:left"><code>v1 &lt;- c(3, 1, TRUE, 2+3i)</code> <br> <code>v2 &lt;&lt;- c(3, 1, TRUE, 2+3i)</code> <br> <code>v3 = c(3, 1, TRUE, 2+3i)</code> <br> <code>print(v1)</code> <br> <code>print(v2)</code> <br> <code>print(v3)</code> <br> 它产生以下结果： <br> <code>[1] 3+0i 1+0i 1+0i 2+3i</code> <br> <code>[1] 3+0i 1+0i 1+0i 2+3i</code> <br> <code>[1] 3+0i 1+0i 1+0i 2+3i</code></td></tr><tr><td style="text-align:center">-&gt; <br> -&gt;&gt;</td><td style="text-align:center">称为右分配</td><td style="text-align:left"><code>c(3, 1, TRUE, 2+3i) -&gt; v1</code> <br> <code>c(3, 1, TRUE, 2+3i) -&gt;&gt; v2</code> <br> <code>print(v1)</code> <br> <code>print(v2)</code> <br> 它产生以下结果： <br> <code>[1] 3+0i 1+0i 1+0i 2+3i</code> <br> <code>[1] 3+0i 1+0i 1+0i 2+3i</code></td></tr></tbody></table></div><h3 id="其他运算符"><a href="#其他运算符" class="headerlink" title="其他运算符"></a>其他运算符</h3><p>这些运算符用于特定目的，而不是一般的数学或逻辑计算。</p><div class="table-container"><table><thead><tr><th style="text-align:center">运算符</th><th style="text-align:center">描述</th><th style="text-align:left">实例</th></tr></thead><tbody><tr><td style="text-align:center">:</td><td style="text-align:center">冒号运算符。它为向量按顺序创建一系列数字。</td><td style="text-align:left"><code>v &lt;- 2:8</code> <br> <code>print(v)</code> <br> 它产生以下结果： <br> <code>[1] 2 3 4 5 6 7 8</code></td></tr><tr><td style="text-align:center">%in%</td><td style="text-align:center">此运算符用于标识元素是否属于向量。</td><td style="text-align:left"><code>v1 &lt;- 8</code> <br> <code>v2 &lt;- 12</code> <br> <code>t &lt;- 1:10</code> <br> <code>print(v1 %in% t)</code> <br> <code>print(v2 %in% t)</code> <br> 它产生以下结果： <br> <code>[1] TRUE</code> <br> <code>[1] FALSE</code></td></tr><tr><td style="text-align:center">%*%</td><td style="text-align:center">此运算符用于将矩阵与其转置相乘。</td><td style="text-align:left"><code>M = matrix(c(2, 6, 5, 1, 10, 4),</code> <br> <code>nrow = 2, ncol = 3, byrow = TRUE)</code> <br> <code>t = M %*% t(M)</code> <br> <code>print(t)</code> <br> 它产生以下结果： <br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<code>[,1] [,2]</code> <br> <code>[1,]</code>&nbsp;&nbsp;&nbsp;&nbsp;<code>65</code>&nbsp;&nbsp;&nbsp;&nbsp;<code>82</code> <br> <code>[2,]</code>&nbsp;&nbsp;&nbsp;&nbsp;<code>82</code>&nbsp;&nbsp;&nbsp;&nbsp;<code>117</code></td></tr></tbody></table></div><h2 id="包"><a href="#包" class="headerlink" title="包"></a>包</h2><p>R 语言的包是 R 函数，编译代码和样本数据的集合。它们存储在 R 语言环境中名为 <code>library</code> 的目录下。默认情况下，R 语言在安装期间安装一组软件包。随后添加更多包，当它们用于某些特定目的时。当我们启动 R 语言控制台时，默认情况下只有默认包可用。已经安装的其他软件包必须显式加载以供将要使用它们的 R 语言程序使用。</p><p>所有可用的 R 语言包都列在 <a href="https://cran.r-project.org/web/packages/available_packages_by_name.html">R 语言的包</a>。</p><p>下面是用于检查，验证和使用 R 包的命令列表。</p><h3 id="检查可用-R-语言的包"><a href="#检查可用-R-语言的包" class="headerlink" title="检查可用 R 语言的包"></a>检查可用 R 语言的包</h3><p>获取包含 R 包的库位置<figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">.libPaths()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果。它可能会根据您的电脑的本地设置而有所不同。</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;C:/Program Files/R/R-3.2.2/library&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="获取已安装的所有软件包列表"><a href="#获取已安装的所有软件包列表" class="headerlink" title="获取已安装的所有软件包列表"></a>获取已安装的所有软件包列表</h3><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">library()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果。它可能会根据您的电脑的本地设置而有所不同。</span></span><br><span class="line"></span><br><span class="line">Packages <span class="keyword">in</span> library ‘C:/Program Files/R/R-<span class="number">3.2</span>.2/library’:</span><br><span class="line"></span><br><span class="line">base                    The R Base Package</span><br><span class="line">boot                    Bootstrap Functions (Originally by Angelo Canty</span><br><span class="line">                        <span class="keyword">for</span> S)</span><br><span class="line"><span class="built_in">class</span>                   Functions <span class="keyword">for</span> Classification</span><br><span class="line">cluster                 <span class="string">&quot;Finding Groups in Data&quot;</span>: Cluster Analysis</span><br><span class="line">                        Extended Rousseeuw et al.</span><br><span class="line">codetools               Code Analysis Tools <span class="keyword">for</span> R</span><br><span class="line">compiler                The R Compiler Package</span><br></pre></td></tr></table></figure></p><p>获取当前在 R 环境中加载的所有包</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">search()</span><br><span class="line">当我们执行上述代码时，它产生了以下结果。它会根据你的个人电脑的本地设置而异。</span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;.GlobalEnv&quot;</span>        <span class="string">&quot;package:stats&quot;</span>     <span class="string">&quot;package:graphics&quot;</span></span><br><span class="line">[<span class="number">4</span>] <span class="string">&quot;package:grDevices&quot;</span> <span class="string">&quot;package:utils&quot;</span>     <span class="string">&quot;package:datasets&quot;</span></span><br><span class="line">[<span class="number">7</span>] <span class="string">&quot;package:methods&quot;</span>   <span class="string">&quot;Autoloads&quot;</span>         <span class="string">&quot;package:base&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="安装一个新的软件包"><a href="#安装一个新的软件包" class="headerlink" title="安装一个新的软件包"></a>安装一个新的软件包</h3><p>有两种方法来添加新的 R 包。一个是直接从 CRAN 目录安装，另一个是将软件包下载到本地系统并手动安装它。</p><h4 id="直接从-CRAN-安装"><a href="#直接从-CRAN-安装" class="headerlink" title="直接从 CRAN 安装"></a>直接从 CRAN 安装</h4><p>以下命令直接从 CRAN 网页获取软件包，并将软件包安装在 R 环境中。可能会提示您选择最近的镜像。根据您的位置选择一个。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">install.packages(<span class="string">&quot;Package Name&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Install the package named &quot;XML&quot;.</span></span><br><span class="line">install.packages(<span class="string">&quot;XML&quot;</span>)</span><br></pre></td></tr></table></figure></p><h4 id="手动安装包"><a href="#手动安装包" class="headerlink" title="手动安装包"></a>手动安装包</h4><p>转到链接 <a href="https://cran.r-project.org/web/packages/available_packages_by_name.html">R Packages</a> 下载所需的包。将包作为 .zip 文件保存在本地系统中的适当位置。</p><p>现在您可以运行以下命令在 R 环境中安装此软件包。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">install.packages(file_name_with_path, repos = <span class="literal">NULL</span>, type = <span class="string">&quot;source&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Install the package named &quot;XML&quot;</span></span><br><span class="line">install.packages(<span class="string">&quot;E:/XML_3.98-1.3.zip&quot;</span>, repos = <span class="literal">NULL</span>, type = <span class="string">&quot;source&quot;</span>)</span><br></pre></td></tr></table></figure></p><h4 id="装载包到库中"><a href="#装载包到库中" class="headerlink" title="装载包到库中"></a>装载包到库中</h4><p>在包可以在代码中使用之前，必须将其加载到当前 R 环境中。您还需要加载先前已安装但在当前环境中不可用的软件包。</p><p>使用以下命令加载包：<figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">library(<span class="string">&quot;package Name&quot;</span>, lib.loc = <span class="string">&quot;path to library&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Load the package named &quot;XML&quot;</span></span><br><span class="line">install.packages(<span class="string">&quot;E:/XML_3.98-1.3.zip&quot;</span>, repos = <span class="literal">NULL</span>, type = <span class="string">&quot;source&quot;</span>)</span><br></pre></td></tr></table></figure></p><h2 id="决策"><a href="#决策" class="headerlink" title="决策"></a>决策</h2><p>决策结构要求程序员指定要由程序评估或测试的一个或多个条件，以及如果条件被确定为真则要执行的一个或多个语句，如果条件为假则执行其他语句。</p><p>R 提供以下类型的决策语句。</p><ul><li>if 语句</li><li>if ... else 语句</li><li>switch 语句</li></ul><h3 id="if-语句"><a href="#if-语句" class="headerlink" title="if 语句"></a>if 语句</h3><p>if 语句由一个布尔表达式后跟一个或多个语句组成。</p><p><strong>语法</strong></p><p>在 R 中创建 <code>if</code> 语句的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(boolean_expression) &#123;</span><br><span class="line">   <span class="comment"># statement(s) will execute if the boolean expression is true.</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>如果布尔表达式的计算结果为 <code>true</code>，那么 <code>if</code> 语句中的代码块将被执行。如果布尔表达式的计算结果为 <code>false</code>，那么第一组代码在 if 语句结束之后（在结束大括号之后）将被执行。</p><p><strong>流程图</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_course/if.jpg" alt="if 语句"></p><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">x &lt;- 30L</span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">is.integer</span>(x)) &#123;</span><br><span class="line">   print(<span class="string">&quot;X is an Integer&quot;</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当上面的代码被编译和执行时，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;X is an Integer&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="if-else-语句"><a href="#if-else-语句" class="headerlink" title="if...else 语句"></a>if...else 语句</h3><p><code>if</code> 语句后面可以是一个可选的 <code>else</code> 语句，当布尔表达式为 <code>false</code> 时执行。</p><p><strong>语法</strong></p><p>在 R 中创建 if ... else 语句的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(boolean_expression) &#123;</span><br><span class="line">   <span class="comment"># statement(s) will execute if the boolean expression is true.</span></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">   <span class="comment"># statement(s) will execute if the boolean expression is false.</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>如果布尔表达式的计算结果为真，则将执行 if 代码块，否则将执行代码块。</p><p><strong>流程图</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_course/if_else.jpg" alt="if...else 语句"></p><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">x &lt;- <span class="built_in">c</span>(<span class="string">&quot;what&quot;</span>, <span class="string">&quot;is&quot;</span>, <span class="string">&quot;truth&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="string">&quot;Truth&quot;</span> %in% x) &#123;</span><br><span class="line">   print(<span class="string">&quot;Truth is found&quot;</span>)</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">   print(<span class="string">&quot;Truth is not found&quot;</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当上面的代码被编译和执行时，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Truth is not found&quot;</span></span><br></pre></td></tr></table></figure></p><p>这里 <code>Truth</code> 和 <code>truth</code> 是两个不同的字符串。</p><p><strong>if ... else if ... else 语句</strong></p><p><code>if</code> 语句后面可以跟一个可选的 <code>else if ... else</code> 语句，这对于使用 <code>single if ... else if</code> 语句测试各种条件非常有用。</p><p>当使用 <code>if</code>，<code>else if</code>，<code>else</code> 语句有几点要记住：</p><ul><li>如果可以有零个或一个 <code>else</code>，它必须在任何其他 <code>if</code> 之后。</li><li>一个 <code>if</code> 可以有零个到许多个 <code>else if</code> 和它们必须在 <code>else</code> 之前。</li><li>一旦一个 <code>else</code> 如果成功，没有任何剩余的 <code>else if</code> 或 <code>else</code> 将被测试。</li></ul><p><strong>语法</strong></p><p>在 R 中创建 <code>if ... else if ... else</code>语句的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(boolean_expression <span class="number">1</span>) &#123;</span><br><span class="line">   // Executes when the boolean <span class="built_in">expression</span> <span class="number">1</span> is true.</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span>( boolean_expression <span class="number">2</span>) &#123;</span><br><span class="line">   // Executes when the boolean <span class="built_in">expression</span> <span class="number">2</span> is true.</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span>( boolean_expression <span class="number">3</span>) &#123;</span><br><span class="line">   // Executes when the boolean <span class="built_in">expression</span> <span class="number">3</span> is true.</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">   // executes when none of the above condition is true.</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">x &lt;- <span class="built_in">c</span>(<span class="string">&quot;what&quot;</span>, <span class="string">&quot;is&quot;</span>, <span class="string">&quot;truth&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="string">&quot;Truth&quot;</span> %in% x) &#123;</span><br><span class="line">   print(<span class="string">&quot;Truth is found the first time&quot;</span>)</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;truth&quot;</span> %in% x) &#123;</span><br><span class="line">   print(<span class="string">&quot;truth is found the second time&quot;</span>)</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">   print(<span class="string">&quot;No truth found&quot;</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当上面的代码被编译和执行时，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;truth is found the second time&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="switch-​语句"><a href="#switch-​语句" class="headerlink" title="switch ​语句"></a>switch ​语句</h3><p><code>switch</code> ​语句允许根据值列表测试变量的相等性。每个值都称为大小写，并且针对每种情况检查打开的变量。</p><p><strong>语法</strong></p><p>在 R 中创建 <code>​switch​</code> 语句的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">switch</span>(<span class="built_in">expression</span>, case1, case2, case3....)</span><br></pre></td></tr></table></figure></p><p>以下规则适用于 <code>​switch</code> ​语句：</p><ul><li>如果 <code>​expression​</code> 的值不是字符串，那么它被强制为整数。</li><li>在交换机中可以有任意数量的 <code>​case</code> ​语句。每个案例后面都跟要比较的值和冒号。</li><li>如果整数的值在 ​<code>1</code> ​和 ​<code>nargs() - 1</code>​（参数的最大数目）之间，则对 ​case ​条件的相应元素求值并返回结果。</li><li>如果表达式求值为字符串，那么该字符串与元素的名称匹配。</li><li>如果有多个匹配，则返回第一个匹配元素。</li><li>无默认参数可用。</li><li>在没有匹配的情况下，如果有一个未命名的元素...它的值被返回。（如果有多个这样的参数，则返回错误。）</li></ul><p><strong>流程图</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_course/switch.jpg" alt="switch ​语句"></p><p><strong>实例 1</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">x &lt;- <span class="built_in">switch</span>(</span><br><span class="line">   <span class="number">3</span>,</span><br><span class="line">   <span class="string">&quot;first&quot;</span>,</span><br><span class="line">   <span class="string">&quot;second&quot;</span>,</span><br><span class="line">   <span class="string">&quot;third&quot;</span>,</span><br><span class="line">   <span class="string">&quot;fourth&quot;</span></span><br><span class="line">)</span><br><span class="line">print(x)</span><br><span class="line"><span class="comment"># 当上面的代码被编译和执行时，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;third&quot;</span></span><br></pre></td></tr></table></figure></p><p><strong>实例 2</strong></p><p><code>​runif()​</code> 函数用于生成从 ​<code>0</code> ​到 ​<code>1</code> ​区间范围内的服从正态分布的随机数：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">switch</span>(<span class="number">1</span>, <span class="number">2</span>*<span class="number">3</span>, sd(<span class="number">1</span>:<span class="number">5</span>), runif(<span class="number">3</span>))  <span class="comment">#返回 (2*3, sd(1:5), runif(3)) list 中的第一个成分 </span></span><br><span class="line"><span class="built_in">switch</span>(<span class="number">2</span>, <span class="number">2</span>*<span class="number">3</span>, sd(<span class="number">1</span>:<span class="number">5</span>), runif(<span class="number">3</span>))  <span class="comment">#返回第二个成分</span></span><br><span class="line"><span class="built_in">switch</span>(<span class="number">3</span>, <span class="number">2</span>*<span class="number">3</span>, sd(<span class="number">1</span>:<span class="number">5</span>), runif(<span class="number">3</span>))  <span class="comment">#返回第三个成分</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 当上面的代码被编译和执行时，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">6</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">1.581139</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">0.31508117</span> <span class="number">0.04610938</span> <span class="number">0.19489747</span></span><br></pre></td></tr></table></figure></p><h2 id="循环"><a href="#循环" class="headerlink" title="循环"></a>循环</h2><p>可能有一种情况，当你需要执行一段代码几次。通常，顺序执行语句。首先执行函数中的第一个语句，然后执行第二个语句，依此类推。</p><p>编程语言提供允许更复杂的执行路径的各种控制结构。</p><p>循环语句允许我们多次执行一个语句或一组语句，以下是大多数编程语言中循环语句的一般形式</p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_course/loop.jpg" alt="循环"></p><p><strong>循环控制语句</strong></p><p>循环控制语句从其正常序列改变执行。当执行离开作用域时，在该作用域中创建的所有自动对象都将被销毁。</p><p>R 语言支持以下控制语句。</p><ul><li>break</li><li>next</li></ul><h3 id="Repeat-语句"><a href="#Repeat-语句" class="headerlink" title="Repeat 语句"></a>Repeat 语句</h3><p><code>repeat</code> 循环重复执行相同的代码，直到满足停止条件。</p><p><strong>语法</strong></p><p>在 R 中创建 Repeat 循环的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">repeat</span> &#123; </span><br><span class="line">   commands </span><br><span class="line">   <span class="keyword">if</span>(condition) &#123;</span><br><span class="line">      <span class="keyword">break</span></span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>流程图</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_course/repeat.jpg" alt="Repeat 语句"></p><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">v &lt;- <span class="built_in">c</span>(<span class="string">&quot;Hello&quot;</span>, <span class="string">&quot;loop&quot;</span>)</span><br><span class="line">cnt &lt;- 2</span><br><span class="line"></span><br><span class="line"><span class="keyword">repeat</span> &#123;</span><br><span class="line">   print(v)</span><br><span class="line">   cnt &lt;- cnt+<span class="number">1</span></span><br><span class="line">   </span><br><span class="line">   <span class="keyword">if</span>(cnt &gt; <span class="number">5</span>) &#123;</span><br><span class="line">      <span class="keyword">break</span></span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当上面的代码被编译和执行时，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span> <span class="string">&quot;loop&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span> <span class="string">&quot;loop&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span> <span class="string">&quot;loop&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span> <span class="string">&quot;loop&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="while-语句"><a href="#while-语句" class="headerlink" title="while 语句"></a>while 语句</h3><p><code>while</code> 循环一次又一次地执行相同的代码，直到满足停止条件。</p><p><strong>语法</strong></p><p>在 R 中创建 <code>while</code> 循环的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (test_expression) &#123;</span><br><span class="line">   statement</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>流程图</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_course/while.jpg" alt="while 语句"></p><p><code>while</code> 循环的关键点是循环可能永远不会运行。当条件被测试并且结果为 <code>false</code> 时，循环体将被跳过，<code>while</code> 循环之后的第一条语句将被执行。</p><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">v &lt;- <span class="built_in">c</span>(<span class="string">&quot;Hello&quot;</span>,<span class="string">&quot;while loop&quot;</span>)</span><br><span class="line">cnt &lt;- 2</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (cnt &lt; <span class="number">7</span>) &#123;</span><br><span class="line">   print(v)</span><br><span class="line">   cnt = cnt + <span class="number">1</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当上面的代码被编译和执行时，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span>  <span class="string">&quot;while loop&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span>  <span class="string">&quot;while loop&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span>  <span class="string">&quot;while loop&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span>  <span class="string">&quot;while loop&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span>  <span class="string">&quot;while loop&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="for-语句"><a href="#for-语句" class="headerlink" title="for 语句"></a>for 语句</h3><p><code>for</code> 循环是一种重复控制结构，允许您有效地编写需要执行特定次数的循环。</p><p>R 的 <code>for</code> 循环是特别灵活的，因为它们不限于整数，或者输入中的偶数。我们可以传递字符向量，逻辑向量，列表或表达式。</p><p><strong>语法</strong></p><p>在 R 中创建一个 <code>for</code> 循环语句的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (变量 <span class="keyword">in</span> 条件) &#123;</span><br><span class="line">   循环体</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>流程图</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_course/for.jpg" alt="for 语句"></p><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">v &lt;- <span class="built_in">LETTERS</span>[<span class="number">1</span>:<span class="number">4</span>]</span><br><span class="line"><span class="keyword">for</span> ( i <span class="keyword">in</span> v) &#123;</span><br><span class="line">   print(i)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当上面的代码被编译和执行时，它产生以下结果 </span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;A&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;B&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;C&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;D&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="break-语句"><a href="#break-语句" class="headerlink" title="break 语句"></a>break 语句</h3><p>R 语言中的 <code>break</code> 语句有以下两种用法：</p><p>当在循环中遇到 <code>break</code> 语句时，循环立即终止，并且程序控制在循环之后的下一语句处恢复。它可以用于终止 <code>switch</code> 语句中的情况。语法在 R 中创建 <code>break</code> 语句的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">break</span></span><br></pre></td></tr></table></figure></p><p><strong>流程图</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_course/break.jpg" alt="break 语句"></p><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">v &lt;- <span class="built_in">c</span>(<span class="string">&quot;Hello&quot;</span>,<span class="string">&quot;loop&quot;</span>)</span><br><span class="line">cnt &lt;- 2</span><br><span class="line"></span><br><span class="line"><span class="keyword">repeat</span> &#123;</span><br><span class="line">   print(v)</span><br><span class="line">   cnt &lt;- cnt + <span class="number">1</span></span><br><span class="line"></span><br><span class="line">   <span class="keyword">if</span>(cnt &gt; <span class="number">5</span>) &#123;</span><br><span class="line">      <span class="keyword">break</span></span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当上面的代码被编译和执行时，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span> <span class="string">&quot;loop&quot;</span> </span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span> <span class="string">&quot;loop&quot;</span> </span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span> <span class="string">&quot;loop&quot;</span> </span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello&quot;</span> <span class="string">&quot;loop&quot;</span> </span><br></pre></td></tr></table></figure></p><h3 id="next-语句"><a href="#next-语句" class="headerlink" title="next 语句"></a>next 语句</h3><p>R 语言存在 <code>next</code> 语句，当我们想跳过循环的当前迭代而不终止它时便可使用 <code>next</code>。遇到 <code>next</code> 时，R 解析器跳过本次迭代，并开始循环的下一次迭代。</p><p><strong>语法</strong></p><p>在 R 中创建 <code>next</code> 语句的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">next</span></span><br></pre></td></tr></table></figure></p><p><strong>流程图</strong></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/r_course/next.jpg" alt="next 语句"></p><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">v &lt;- <span class="built_in">LETTERS</span>[<span class="number">1</span>:<span class="number">6</span>]</span><br><span class="line"><span class="keyword">for</span> ( i <span class="keyword">in</span> v) &#123;</span><br><span class="line">   </span><br><span class="line">   <span class="keyword">if</span> (i == <span class="string">&quot;D&quot;</span>) &#123;</span><br><span class="line">      <span class="keyword">next</span></span><br><span class="line">   &#125;</span><br><span class="line">   print(i)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">#当上面的代码被编译和执行时，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;A&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;B&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;C&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;E&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;F&quot;</span></span><br></pre></td></tr></table></figure></p><h2 id="数据重塑"><a href="#数据重塑" class="headerlink" title="数据重塑"></a>数据重塑</h2><p>R 语言中的数据重塑是关于改变数据被组织成行和列的方式。</p><p>大多数时间 R 语言中的数据处理是通过将输入数据作为数据框来完成的。很容易从数据框的行和列中提取数据，但是在某些情况下，我们需要的数据框格式与我们接收数据框的格式不同。R 语言具有许多函数，可以在数据框中拆分，合并和将行更改为列，反之亦然。</p><h3 id="数据框中加入列和行"><a href="#数据框中加入列和行" class="headerlink" title="数据框中加入列和行"></a>数据框中加入列和行</h3><p>我们可以使用 <code>cbind()</code> 函数连接多个向量来创建数据框。此外，我们可以使用 <code>rbind()</code> 函数合并两个数据框。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create vector objects.</span></span><br><span class="line">city &lt;- <span class="built_in">c</span>(<span class="string">&quot;Tampa&quot;</span>, <span class="string">&quot;Seattle&quot;</span>, <span class="string">&quot;Hartford&quot;</span>, <span class="string">&quot;Denver&quot;</span>)</span><br><span class="line">state &lt;- <span class="built_in">c</span>(<span class="string">&quot;FL&quot;</span>, <span class="string">&quot;WA&quot;</span>, <span class="string">&quot;CT&quot;</span>, <span class="string">&quot;CO&quot;</span>)</span><br><span class="line">zipcode &lt;- <span class="built_in">c</span>(<span class="number">33602</span>, <span class="number">98104</span>, <span class="number">06161</span>, <span class="number">80294</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Combine above three vectors into one data frame.</span></span><br><span class="line">addresses &lt;- cbind(city, state, zipcode)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print a header.</span></span><br><span class="line">cat(<span class="string">&quot;# # # # The First data frame&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the data frame.</span></span><br><span class="line">print(addresses)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create another data frame with similar columns</span></span><br><span class="line">new.address &lt;- data.frame(</span><br><span class="line">   city = <span class="built_in">c</span>(<span class="string">&quot;Lowry&quot;</span>, <span class="string">&quot;Charlotte&quot;</span>),</span><br><span class="line">   state = <span class="built_in">c</span>(<span class="string">&quot;CO&quot;</span>, <span class="string">&quot;FL&quot;</span>),</span><br><span class="line">   zipcode = <span class="built_in">c</span>(<span class="string">&quot;80230&quot;</span>, <span class="string">&quot;33949&quot;</span>),</span><br><span class="line">   stringsAsFactors = <span class="literal">FALSE</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print a header.</span></span><br><span class="line">cat(<span class="string">&quot;# # # The Second data frame&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the data frame.</span></span><br><span class="line">print(new.address)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Combine rows form both the data frames.</span></span><br><span class="line">all.addresses &lt;- rbind(addresses, new.address)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print a header.</span></span><br><span class="line">cat(<span class="string">&quot;# # # The combined data frame&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the result.</span></span><br><span class="line">print(all.addresses)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># # # # The First data frame</span></span><br><span class="line">     city       state zipcode</span><br><span class="line">[<span class="number">1</span>,] <span class="string">&quot;Tampa&quot;</span>    <span class="string">&quot;FL&quot;</span>  <span class="string">&quot;33602&quot;</span></span><br><span class="line">[<span class="number">2</span>,] <span class="string">&quot;Seattle&quot;</span>  <span class="string">&quot;WA&quot;</span>  <span class="string">&quot;98104&quot;</span></span><br><span class="line">[<span class="number">3</span>,] <span class="string">&quot;Hartford&quot;</span> <span class="string">&quot;CT&quot;</span>  <span class="string">&quot;6161&quot;</span> </span><br><span class="line">[<span class="number">4</span>,] <span class="string">&quot;Denver&quot;</span>   <span class="string">&quot;CO&quot;</span>  <span class="string">&quot;80294&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># # # The Second data frame</span></span><br><span class="line">       city state zipcode</span><br><span class="line">1     Lowry    CO   <span class="number">80230</span></span><br><span class="line">2 Charlotte    FL   <span class="number">33949</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># # # The combined data frame</span></span><br><span class="line">       city state zipcode</span><br><span class="line">1     Tampa    FL   <span class="number">33602</span></span><br><span class="line">2   Seattle    WA   <span class="number">98104</span></span><br><span class="line">3  Hartford    CT    <span class="number">6161</span></span><br><span class="line">4    Denver    CO   <span class="number">80294</span></span><br><span class="line">5     Lowry    CO   <span class="number">80230</span></span><br><span class="line">6 Charlotte    FL   <span class="number">33949</span></span><br></pre></td></tr></table></figure></p><h3 id="合并数据框"><a href="#合并数据框" class="headerlink" title="合并数据框"></a>合并数据框</h3><p>我们可以使用 <code>merge()</code> 函数合并两个数据框。数据框必须具有相同的列名称，在其上进行合并。</p><p>在下面的例子中，我们考虑使用 <code>library()</code> 函数加载 <code>MASS</code> 第三方包，使用 <code>MASS</code> 中有关 <code>Pima Indian Women</code> 的糖尿病的数据集。我们基于血压（bp）和体重指数（bmi）的值合并两个数据集。在选择这两列用于合并时，其中这两个变量的值在两个数据集中匹配的记录被组合在一起以形成单个数据框。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">library(MASS)</span><br><span class="line">merged.Pima &lt;- merge(x = Pima.te, y = Pima.tr,</span><br><span class="line">   by.x = <span class="built_in">c</span>(<span class="string">&quot;bp&quot;</span>, <span class="string">&quot;bmi&quot;</span>),</span><br><span class="line">   by.y = <span class="built_in">c</span>(<span class="string">&quot;bp&quot;</span>, <span class="string">&quot;bmi&quot;</span>)</span><br><span class="line">)</span><br><span class="line">print(merged.Pima)</span><br><span class="line">nrow(merged.Pima)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">   bp  bmi npreg.x glu.x skin.x ped.x age.x type.x npreg.y glu.y skin.y ped.y age.y type.y</span><br><span class="line">1  <span class="number">60</span> <span class="number">33.8</span>       <span class="number">1</span>   <span class="number">117</span>     <span class="number">23</span> <span class="number">0.466</span>    <span class="number">27</span>     No       <span class="number">2</span>   <span class="number">125</span>     <span class="number">20</span> <span class="number">0.088</span>    <span class="number">31</span>     No</span><br><span class="line">2  <span class="number">64</span> <span class="number">29.7</span>       <span class="number">2</span>    <span class="number">75</span>     <span class="number">24</span> <span class="number">0.370</span>    <span class="number">33</span>     No       <span class="number">2</span>   <span class="number">100</span>     <span class="number">23</span> <span class="number">0.368</span>    <span class="number">21</span>     No</span><br><span class="line">3  <span class="number">64</span> <span class="number">31.2</span>       <span class="number">5</span>   <span class="number">189</span>     <span class="number">33</span> <span class="number">0.583</span>    <span class="number">29</span>    Yes       <span class="number">3</span>   <span class="number">158</span>     <span class="number">13</span> <span class="number">0.295</span>    <span class="number">24</span>     No</span><br><span class="line">4  <span class="number">64</span> <span class="number">33.2</span>       <span class="number">4</span>   <span class="number">117</span>     <span class="number">27</span> <span class="number">0.230</span>    <span class="number">24</span>     No       <span class="number">1</span>    <span class="number">96</span>     <span class="number">27</span> <span class="number">0.289</span>    <span class="number">21</span>     No</span><br><span class="line">5  <span class="number">66</span> <span class="number">38.1</span>       <span class="number">3</span>   <span class="number">115</span>     <span class="number">39</span> <span class="number">0.150</span>    <span class="number">28</span>     No       <span class="number">1</span>   <span class="number">114</span>     <span class="number">36</span> <span class="number">0.289</span>    <span class="number">21</span>     No</span><br><span class="line">6  <span class="number">68</span> <span class="number">38.5</span>       <span class="number">2</span>   <span class="number">100</span>     <span class="number">25</span> <span class="number">0.324</span>    <span class="number">26</span>     No       <span class="number">7</span>   <span class="number">129</span>     <span class="number">49</span> <span class="number">0.439</span>    <span class="number">43</span>    Yes</span><br><span class="line">7  <span class="number">70</span> <span class="number">27.4</span>       <span class="number">1</span>   <span class="number">116</span>     <span class="number">28</span> <span class="number">0.204</span>    <span class="number">21</span>     No       <span class="number">0</span>   <span class="number">124</span>     <span class="number">20</span> <span class="number">0.254</span>    <span class="number">36</span>    Yes</span><br><span class="line">8  <span class="number">70</span> <span class="number">33.1</span>       <span class="number">4</span>    <span class="number">91</span>     <span class="number">32</span> <span class="number">0.446</span>    <span class="number">22</span>     No       <span class="number">9</span>   <span class="number">123</span>     <span class="number">44</span> <span class="number">0.374</span>    <span class="number">40</span>     No</span><br><span class="line">9  <span class="number">70</span> <span class="number">35.4</span>       <span class="number">9</span>   <span class="number">124</span>     <span class="number">33</span> <span class="number">0.282</span>    <span class="number">34</span>     No       <span class="number">6</span>   <span class="number">134</span>     <span class="number">23</span> <span class="number">0.542</span>    <span class="number">29</span>    Yes</span><br><span class="line">10 <span class="number">72</span> <span class="number">25.6</span>       <span class="number">1</span>   <span class="number">157</span>     <span class="number">21</span> <span class="number">0.123</span>    <span class="number">24</span>     No       <span class="number">4</span>    <span class="number">99</span>     <span class="number">17</span> <span class="number">0.294</span>    <span class="number">28</span>     No</span><br><span class="line">11 <span class="number">72</span> <span class="number">37.7</span>       <span class="number">5</span>    <span class="number">95</span>     <span class="number">33</span> <span class="number">0.370</span>    <span class="number">27</span>     No       <span class="number">6</span>   <span class="number">103</span>     <span class="number">32</span> <span class="number">0.324</span>    <span class="number">55</span>     No</span><br><span class="line">12 <span class="number">74</span> <span class="number">25.9</span>       <span class="number">9</span>   <span class="number">134</span>     <span class="number">33</span> <span class="number">0.460</span>    <span class="number">81</span>     No       <span class="number">8</span>   <span class="number">126</span>     <span class="number">38</span> <span class="number">0.162</span>    <span class="number">39</span>     No</span><br><span class="line">13 <span class="number">74</span> <span class="number">25.9</span>       <span class="number">1</span>    <span class="number">95</span>     <span class="number">21</span> <span class="number">0.673</span>    <span class="number">36</span>     No       <span class="number">8</span>   <span class="number">126</span>     <span class="number">38</span> <span class="number">0.162</span>    <span class="number">39</span>     No</span><br><span class="line">14 <span class="number">78</span> <span class="number">27.6</span>       <span class="number">5</span>    <span class="number">88</span>     <span class="number">30</span> <span class="number">0.258</span>    <span class="number">37</span>     No       <span class="number">6</span>   <span class="number">125</span>     <span class="number">31</span> <span class="number">0.565</span>    <span class="number">49</span>    Yes</span><br><span class="line">15 <span class="number">78</span> <span class="number">27.6</span>      <span class="number">10</span>   <span class="number">122</span>     <span class="number">31</span> <span class="number">0.512</span>    <span class="number">45</span>     No       <span class="number">6</span>   <span class="number">125</span>     <span class="number">31</span> <span class="number">0.565</span>    <span class="number">49</span>    Yes</span><br><span class="line">16 <span class="number">78</span> <span class="number">39.4</span>       <span class="number">2</span>   <span class="number">112</span>     <span class="number">50</span> <span class="number">0.175</span>    <span class="number">24</span>     No       <span class="number">4</span>   <span class="number">112</span>     <span class="number">40</span> <span class="number">0.236</span>    <span class="number">38</span>     No</span><br><span class="line">17 <span class="number">88</span> <span class="number">34.5</span>       <span class="number">1</span>   <span class="number">117</span>     <span class="number">24</span> <span class="number">0.403</span>    <span class="number">40</span>    Yes       <span class="number">4</span>   <span class="number">127</span>     <span class="number">11</span> <span class="number">0.598</span>    <span class="number">28</span>     No</span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">17</span></span><br></pre></td></tr></table></figure></p><p>有时，电子表格数据的格式很紧凑，可以给出每个主题的协变量，然后是该主题的所有观测值。R 的建函数需要在单个列中进行观察。考虑以下来自重复 <code>MRI</code> 脑测量的数据样本：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Status   Age    V1     V2     V3    V4</span><br><span class="line">     P <span class="number">23646</span> <span class="number">45190</span>  <span class="number">50333</span>  <span class="number">55166</span> <span class="number">56271</span></span><br><span class="line">    CC <span class="number">26174</span> <span class="number">35535</span>  <span class="number">38227</span>  <span class="number">37911</span> <span class="number">41184</span></span><br><span class="line">    CC <span class="number">27723</span> <span class="number">25691</span>  <span class="number">25712</span>  <span class="number">26144</span> <span class="number">26398</span></span><br><span class="line">    CC <span class="number">27193</span> <span class="number">30949</span>  <span class="number">29693</span>  <span class="number">29754</span> <span class="number">30772</span></span><br><span class="line">    CC <span class="number">24370</span> <span class="number">50542</span>  <span class="number">51966</span>  <span class="number">54341</span> <span class="number">54273</span></span><br><span class="line">    CC <span class="number">28359</span> <span class="number">58591</span>  <span class="number">58803</span>  <span class="number">59435</span> <span class="number">61292</span></span><br><span class="line">    CC <span class="number">25136</span> <span class="number">45801</span>  <span class="number">45389</span>  <span class="number">47197</span> <span class="number">47126</span></span><br></pre></td></tr></table></figure></p><p>在每个主题上有两个协变量和多达四个测量值。数据从 Excel 导出为 <code>mr.csv</code> 文件。</p><p>我们可以使用堆栈来帮助操纵这些数据以给出单个响应。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">zz &lt;- read.csv(<span class="string">&quot;mr.csv&quot;</span>, strip.white = <span class="literal">TRUE</span>)</span><br><span class="line">zzz &lt;- cbind(zz[gl(nrow(zz), <span class="number">1</span>, <span class="number">4</span>*nrow(zz)), <span class="number">1</span>:<span class="number">2</span>], stack(zz[, <span class="number">3</span>:<span class="number">6</span>]))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">      Status   Age values ind</span><br><span class="line">X1         P <span class="number">23646</span>  <span class="number">45190</span>  V1</span><br><span class="line">X2        CC <span class="number">26174</span>  <span class="number">35535</span>  V1</span><br><span class="line">X3        CC <span class="number">27723</span>  <span class="number">25691</span>  V1</span><br><span class="line">X4        CC <span class="number">27193</span>  <span class="number">30949</span>  V1</span><br><span class="line">X5        CC <span class="number">24370</span>  <span class="number">50542</span>  V1</span><br><span class="line">X6        CC <span class="number">28359</span>  <span class="number">58591</span>  V1</span><br><span class="line">X7        CC <span class="number">25136</span>  <span class="number">45801</span>  V1</span><br><span class="line">X11        P <span class="number">23646</span>  <span class="number">50333</span>  V2</span><br><span class="line">...</span><br></pre></td></tr></table></figure></p><p>函数 <code>unstack</code> 的方向相反，可能对导出数据很有用。</p><p>另一种方法是使用函数重塑</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">reshape(zz, idvar=<span class="string">&quot;id&quot;</span>, timevar=<span class="string">&quot;var&quot;</span>, varying=<span class="built_in">list</span>(<span class="built_in">c</span>(<span class="string">&quot;V1&quot;</span>, <span class="string">&quot;V2&quot;</span>, <span class="string">&quot;V3&quot;</span>, <span class="string">&quot;V4&quot;</span>)), direction=<span class="string">&quot;long&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">    Status   Age var    V1 id</span><br><span class="line">1.1      P <span class="number">23646</span>   <span class="number">1</span> <span class="number">45190</span>  <span class="number">1</span></span><br><span class="line">2.1     CC <span class="number">26174</span>   <span class="number">1</span> <span class="number">35535</span>  <span class="number">2</span></span><br><span class="line">3.1     CC <span class="number">27723</span>   <span class="number">1</span> <span class="number">25691</span>  <span class="number">3</span></span><br><span class="line">4.1     CC <span class="number">27193</span>   <span class="number">1</span> <span class="number">30949</span>  <span class="number">4</span></span><br><span class="line">5.1     CC <span class="number">24370</span>   <span class="number">1</span> <span class="number">50542</span>  <span class="number">5</span></span><br><span class="line">6.1     CC <span class="number">28359</span>   <span class="number">1</span> <span class="number">58591</span>  <span class="number">6</span></span><br><span class="line">7.1     CC <span class="number">25136</span>   <span class="number">1</span> <span class="number">45801</span>  <span class="number">7</span></span><br><span class="line">1.2      P <span class="number">23646</span>   <span class="number">2</span> <span class="number">50333</span>  <span class="number">1</span></span><br><span class="line">2.2     CC <span class="number">26174</span>   <span class="number">2</span> <span class="number">38227</span>  <span class="number">2</span></span><br><span class="line">...</span><br></pre></td></tr></table></figure></p><p>重塑函数的语法比堆栈更复杂，但可以用于 <code>long</code> 表单中不止一列的数据。如果 <code>direction=&quot;width&quot;</code>，重塑还可以执行相反的转换。</p><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><p>函数是一组组合在一起以执行特定任务的语句。R 语言具有大量内置函数，用户可以创建自己的函数。</p><p>在 R 语言中，函数是一个对象，因此 R 语言解释器能够将控制传递给函数，以及函数完成动作所需的参数。</p><p>该函数依次执行其任务并将控制返回到解释器以及可以存储在其他对象中的任何结果。</p><h3 id="函数定义"><a href="#函数定义" class="headerlink" title="函数定义"></a>函数定义</h3><p>使用关键字函数创建 R 语言的函数。R 语言的函数定义的基本语法如下</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">function_name &lt;- <span class="keyword">function</span>(arg_1, arg_2, ...) &#123;</span><br><span class="line">   Function body</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="函数组件"><a href="#函数组件" class="headerlink" title="函数组件"></a>函数组件</h3><p>函数的不同部分：</p><ul><li><strong>函数名称</strong>：这是函数的实际名称。它作为具有此名称的对象存储在 R 环境中。</li><li><strong>参数</strong>：参数是一个占位符。当函数被调用时，你传递一个值到参数。参数是可选的，也就是说，一个函数可能不包含参数。参数也可以有默认值。</li><li><strong>函数体</strong>：函数体包含定义函数的语句集合。</li><li><strong>返回值</strong>：函数的返回值是要评估的函数体中的最后一个表达式。</li></ul><p>R 语言有许多内置函数，可以在程序中直接调用而无需先定义它们。我们还可以创建和使用我们自己的函数，称为用户定义的函数。</p><h3 id="内置函数"><a href="#内置函数" class="headerlink" title="内置函数"></a>内置函数</h3><p>内置函数的简单示例是 <code>seq()</code>，<code>mean()</code>，<code>max()</code>，<code>sum(x)</code> 和 <code>paste(...)</code> 等。它们由用户编写的程序直接调用。您可以参考最广泛使用的 R 函数。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a sequence of numbers from 32 to 44.</span></span><br><span class="line">print(seq(<span class="number">32</span>, <span class="number">44</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Find mean of numbers from 25 to 82.</span></span><br><span class="line">print(mean(<span class="number">25</span>:<span class="number">82</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Find sum of numbers frm 41 to 68.</span></span><br><span class="line">print(<span class="built_in">sum</span>(<span class="number">41</span>:<span class="number">68</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">32</span> <span class="number">33</span> <span class="number">34</span> <span class="number">35</span> <span class="number">36</span> <span class="number">37</span> <span class="number">38</span> <span class="number">39</span> <span class="number">40</span> <span class="number">41</span> <span class="number">42</span> <span class="number">43</span> <span class="number">44</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">53.5</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">1526</span></span><br></pre></td></tr></table></figure></p><h3 id="用户定义的函数"><a href="#用户定义的函数" class="headerlink" title="用户定义的函数"></a>用户定义的函数</h3><p>我们可以在 R 语言中创建用户定义的函数。它们特定于用户想要的，一旦创建，它们就可以像内置函数一样使用。下面是一个创建和使用函数的例子。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a function to print squares of numbers in sequence.</span></span><br><span class="line">new.function &lt;- <span class="keyword">function</span>(a) &#123;</span><br><span class="line">   <span class="keyword">for</span>(i <span class="keyword">in</span> <span class="number">1</span>:a) &#123;</span><br><span class="line">      b &lt;- i^<span class="number">2</span></span><br><span class="line">      print(b)</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="调用函数"><a href="#调用函数" class="headerlink" title="调用函数"></a>调用函数</h3><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a function to print squares of numbers in sequence.</span></span><br><span class="line">new.function &lt;- <span class="keyword">function</span>(a) &#123;</span><br><span class="line">   <span class="keyword">for</span>(i <span class="keyword">in</span> <span class="number">1</span>:a) &#123;</span><br><span class="line">      b &lt;- i^<span class="number">2</span></span><br><span class="line">      print(b)</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Call the function new.function supplying 6 as an argument.</span></span><br><span class="line">new.function(<span class="number">4</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">1</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">4</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">9</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">16</span></span><br></pre></td></tr></table></figure></p><h2 id="调用没有参数的函数"><a href="#调用没有参数的函数" class="headerlink" title="调用没有参数的函数"></a>调用没有参数的函数</h2><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a function without an argument.</span></span><br><span class="line">new.function &lt;- <span class="keyword">function</span>() &#123;</span><br><span class="line">   <span class="keyword">for</span>(i <span class="keyword">in</span> <span class="number">1</span>:<span class="number">5</span>) &#123;</span><br><span class="line">      print(i^<span class="number">2</span>)</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Call the function without supplying an argument.</span></span><br><span class="line">new.function()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">1</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">4</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">9</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">16</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">25</span></span><br></pre></td></tr></table></figure></p><h3 id="使用参数值调用函数（按位置和名称）"><a href="#使用参数值调用函数（按位置和名称）" class="headerlink" title="使用参数值调用函数（按位置和名称）"></a>使用参数值调用函数（按位置和名称）</h3><p>函数调用的参数可以按照函数中定义的顺序提供，也可以以不同的顺序提供，但分配给参数的名称。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a function with arguments.</span></span><br><span class="line">new.function &lt;- <span class="keyword">function</span>(a, b, <span class="built_in">c</span>) &#123;</span><br><span class="line">   result &lt;- a * b + <span class="built_in">c</span></span><br><span class="line">   print(result)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Call the function by position of arguments.</span></span><br><span class="line">new.function(<span class="number">5</span>, <span class="number">3</span>, <span class="number">11</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Call the function by names of the arguments.</span></span><br><span class="line">new.function(a = <span class="number">11</span>, b = <span class="number">5</span>, <span class="built_in">c</span> = <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">26</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">58</span></span><br></pre></td></tr></table></figure></p><h3 id="使用默认参数调用函数"><a href="#使用默认参数调用函数" class="headerlink" title="使用默认参数调用函数"></a>使用默认参数调用函数</h3><p>我们可以在函数定义中定义参数的值，并调用函数而不提供任何参数以获取默认结果。但是我们也可以通过提供参数的新值来获得非默认结果来调用这样的函数。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a function with arguments.</span></span><br><span class="line">new.function &lt;- <span class="keyword">function</span>(a = <span class="number">3</span>, b = <span class="number">6</span>) &#123;</span><br><span class="line">   result &lt;- a * b</span><br><span class="line">   print(result)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Call the function without giving any argument.</span></span><br><span class="line">new.function()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Call the function with giving new values of the argument.</span></span><br><span class="line">new.function(<span class="number">9</span>, <span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">18</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">45</span></span><br></pre></td></tr></table></figure></p><h3 id="函数的延迟计算"><a href="#函数的延迟计算" class="headerlink" title="函数的延迟计算"></a>函数的延迟计算</h3><p>对函数的参数进行延迟评估，这意味着它们只有在函数体需要时才进行评估。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a function with arguments.</span></span><br><span class="line">new.function &lt;- <span class="keyword">function</span>(a, b) &#123;</span><br><span class="line">   print(a^<span class="number">2</span>)</span><br><span class="line">   print(a)</span><br><span class="line">   print(b)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Evaluate the function without supplying one of the arguments.</span></span><br><span class="line">new.function(<span class="number">6</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">36</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">6</span></span><br><span class="line">Error <span class="keyword">in</span> print(b) : argument <span class="string">&quot;b&quot;</span> is <span class="built_in">missing</span>, with no default</span><br></pre></td></tr></table></figure></p><h2 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h2><p>在 R 语言中的单引号或双引号对中写入的任何值都被视为字符串。R 语言存储的每个字符串都在双引号内，即使是使用单引号创建的依旧如此。</p><h3 id="字符串构造规则"><a href="#字符串构造规则" class="headerlink" title="字符串构造规则"></a>字符串构造规则</h3><p>在字符串的开头和结尾的引号应该是两个双引号或两个单引号。它们不能被混合。</p><ul><li>双引号可以插入以单引号开头和结尾的字符串中。</li><li>单引号可以插入以双引号开头和结尾的字符串中。</li><li>双引号不能插入以双引号开头和结尾的字符串中。</li><li>单引号不能插入以单引号开头和结尾的字符串中。</li></ul><p><strong>有效字符串的示例</strong></p><p>以下示例阐明了在 R 语言中创建字符串的规则。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">a &lt;- <span class="string">&#x27;Start and end with single quote&#x27;</span></span><br><span class="line">print(a)</span><br><span class="line"></span><br><span class="line">b &lt;- <span class="string">&quot;Start and end with double quotes&quot;</span></span><br><span class="line">print(b)</span><br><span class="line"></span><br><span class="line">c &lt;- <span class="string">&quot;single quote &#x27; in between double quotes&quot;</span></span><br><span class="line">print(<span class="built_in">c</span>)</span><br><span class="line"></span><br><span class="line">d &lt;- <span class="string">&#x27;Double quotes &quot; in between single quote&#x27;</span></span><br><span class="line">print(d)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Start and end with single quote&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Start and end with double quotes&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;single quote &#x27; in between double quote&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Double quote &quot;</span> <span class="keyword">in</span> between single <span class="built_in">quote</span><span class="string">&quot;</span></span><br></pre></td></tr></table></figure></p><p><strong>无效字符串的示例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">e &lt;- <span class="string">&#x27;Mixed quotes&quot;</span></span><br><span class="line"><span class="string">print(e)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">f &lt;- &#x27;</span>Single <span class="built_in">quote</span> <span class="string">&#x27; inside single quote&#x27;</span></span><br><span class="line">print(f)</span><br><span class="line"></span><br><span class="line">g &lt;- <span class="string">&quot;Double quotes &quot;</span> inside double quotes<span class="string">&quot;</span></span><br><span class="line"><span class="string">print(g)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># 当我们运行脚本失败给下面的结果：</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">...: unexpected INCOMPLETE_STRING</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">.... unexpected symbol </span></span><br><span class="line"><span class="string">1: f &lt;- &#x27;Single quote &#x27; inside</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">unexpected symbol</span></span><br><span class="line"><span class="string">1: g &lt;- &quot;</span>Double quotes <span class="string">&quot; inside</span></span><br></pre></td></tr></table></figure></p><h3 id="字符串操作"><a href="#字符串操作" class="headerlink" title="字符串操作"></a>字符串操作</h3><h4 id="连接字符串"><a href="#连接字符串" class="headerlink" title="连接字符串"></a>连接字符串</h4><p>R 语言中的许多字符串使用 <code>paste()</code> 函数组合。它可以采取任何数量的参数组合在一起。</p><p><strong>语法</strong></p><p>函数的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">paste(..., sep = <span class="string">&quot; &quot;</span>, collapse = <span class="literal">NULL</span>)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>...</code>表示要组合的任意数量的自变量。</li><li><code>sep</code> 表示参数之间的任何分隔符。它是可选的。</li><li><code>collapse</code> 用于消除两个字符串之间的空格。但不是一个字符串的两个字内的空间。</li></ul><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">a &lt;- <span class="string">&quot;Hello&quot;</span></span><br><span class="line">b &lt;- <span class="string">&#x27;How&#x27;</span></span><br><span class="line">c &lt;- <span class="string">&quot;are you? &quot;</span></span><br><span class="line"></span><br><span class="line">print(paste(a, b, <span class="built_in">c</span>))</span><br><span class="line">print(paste(a, b, <span class="built_in">c</span>, sep = <span class="string">&quot;-&quot;</span>))</span><br><span class="line">print(paste(a, b, <span class="built_in">c</span>, sep = <span class="string">&quot;&quot;</span>, collapse = <span class="string">&quot;&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello How are you? &quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello-How-are you? &quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;HelloHoware you? &quot;</span></span><br></pre></td></tr></table></figure></p><h4 id="格式化数字和字符串"><a href="#格式化数字和字符串" class="headerlink" title="格式化数字和字符串"></a>格式化数字和字符串</h4><p>可以使用 <code>format()</code> 函数将数字和字符串格式化为特定样式。</p><p><strong>语法</strong></p><p>函数的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">format(x, digits, nsmall, scientific, width, justify = <span class="built_in">c</span>(<span class="string">&quot;left&quot;</span>, <span class="string">&quot;right&quot;</span>, <span class="string">&quot;centre&quot;</span>, <span class="string">&quot;none&quot;</span>))</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是向量输入。</li><li><code>digits</code> 是显示的总位数。</li><li><code>nsmall</code> 是小数点右边的最小位数。</li><li><code>scientific</code> 设置为 <code>TRUE</code> 以科学记数法显示。</li><li><code>width</code> 指示通过在开始处填充空白来显示的最小宽度。</li><li><code>justify</code> 是字符串向左，右或中心的显示。</li></ul><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Total number of digits displayed. Last digit rounded off.</span></span><br><span class="line">result &lt;- format(<span class="number">23.123456789</span>, digits = <span class="number">9</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Display numbers in scientific notation.</span></span><br><span class="line">result &lt;- format(<span class="built_in">c</span>(<span class="number">6</span>, <span class="number">13.14521</span>), scientific = <span class="literal">TRUE</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># The minimum number of digits to the right of the decimal point.</span></span><br><span class="line">result &lt;- format(<span class="number">23.47</span>, nsmall = <span class="number">5</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Format treats everything as a string.</span></span><br><span class="line">result &lt;- format(<span class="number">6</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Numbers are padded with blank in the beginning for width.</span></span><br><span class="line">result &lt;- format(<span class="number">13.7</span>, width = <span class="number">6</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Left justify strings.</span></span><br><span class="line">result &lt;- format(<span class="string">&quot;Hello&quot;</span>, width = <span class="number">8</span>, justify = <span class="string">&quot;l&quot;</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Justfy string with center.</span></span><br><span class="line">result &lt;- format(<span class="string">&quot;Hello&quot;</span>, width = <span class="number">8</span>, justify = <span class="string">&quot;c&quot;</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;23.1234568&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;6.000000e+00&quot;</span> <span class="string">&quot;1.314521e+01&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;23.47000&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;6&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;  13.7&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Hello   &quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot; Hello  &quot;</span></span><br></pre></td></tr></table></figure></p><h4 id="计算字符串中的字符数"><a href="#计算字符串中的字符数" class="headerlink" title="计算字符串中的字符数"></a>计算字符串中的字符数</h4><p><code>nchar()</code> 函数可以计算字符串中包含空格的字符数。</p><p><strong>语法</strong></p><p>函数的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nchar(x)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是向量输入。</li></ul><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">result &lt;- nchar(<span class="string">&quot;Count the number of characters&quot;</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">30</span></span><br></pre></td></tr></table></figure></p><h4 id="更改大小写"><a href="#更改大小写" class="headerlink" title="更改大小写"></a>更改大小写</h4><p>使用 <code>toupper()</code> 和 <code>tolower()</code> 函数可以改变字符串的字符的大小写。</p><p><strong>语法</strong></p><p>函数的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">toupper(x)</span><br><span class="line">tolower(x)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是向量输入。</li></ul><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Changing to Upper case.</span></span><br><span class="line">result &lt;- toupper(<span class="string">&quot;Changing To Upper&quot;</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Changing to lower case.</span></span><br><span class="line">result &lt;- tolower(<span class="string">&quot;Changing To Lower&quot;</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;CHANGING TO UPPER&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;changing to lower&quot;</span></span><br></pre></td></tr></table></figure></p><h4 id="提取字符串"><a href="#提取字符串" class="headerlink" title="提取字符串"></a>提取字符串</h4><p><code>substring()</code> 函数可以用于提取字符串的部分。</p><p><strong>语法</strong></p><p>函数的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">substring(x, first, last)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是字符向量输入。</li><li><code>first</code> 是要提取的第一个字符的位置。</li><li><code>last</code> 是要提取的最后一个字符的位置。</li></ul><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Extract characters from 5th to 7th position.</span></span><br><span class="line">result &lt;- substring(<span class="string">&quot;Extract&quot;</span>, <span class="number">5</span>, <span class="number">7</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;act&quot;</span></span><br></pre></td></tr></table></figure></p><h2 id="向量"><a href="#向量" class="headerlink" title="向量"></a>向量</h2><p>向量是最基本的 R 语言数据对象，有六种类型的原子向量。它们是逻辑，整数，双精度，复杂，字符和原始。</p><h3 id="创建向量"><a href="#创建向量" class="headerlink" title="创建向量"></a>创建向量</h3><h4 id="单元素向量"><a href="#单元素向量" class="headerlink" title="单元素向量"></a>单元素向量</h4><p>即使在 R 语言中只写入一个值，它也将成为长度为 <code>1</code> 的向量，并且属于上述向量类型之一。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Atomic vector of type character.</span></span><br><span class="line">print(<span class="string">&quot;abc&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment"># Atomic vector of type double.</span></span><br><span class="line">print(<span class="number">12.5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Atomic vector of type integer.</span></span><br><span class="line">print(<span class="number">63L</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Atomic vector of type logical.</span></span><br><span class="line">print(<span class="literal">TRUE</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Atomic vector of type complex.</span></span><br><span class="line">print(<span class="number">2</span>+<span class="number">3i</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Atomic vector of type raw.</span></span><br><span class="line">print(charToRaw(<span class="string">&#x27;hello&#x27;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;abc&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">12.5</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">63</span></span><br><span class="line">[<span class="number">1</span>] <span class="literal">TRUE</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">2</span>+<span class="number">3i</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">68</span> <span class="number">65</span> <span class="number">6</span><span class="built_in">c</span> <span class="number">6</span><span class="built_in">c</span> <span class="number">6</span>f</span><br></pre></td></tr></table></figure></p><h4 id="多元素向量"><a href="#多元素向量" class="headerlink" title="多元素向量"></a>多元素向量</h4><p>对数值数据使用冒号运算符</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Creating a sequence from 5 to 13.</span></span><br><span class="line">v &lt;- 5:<span class="number">13</span></span><br><span class="line">print(v)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Creating a sequence from 6.6 to 12.6.</span></span><br><span class="line">v &lt;- 6.6:<span class="number">12.6</span></span><br><span class="line">print(v)</span><br><span class="line"></span><br><span class="line"><span class="comment"># If the final element specified does not belong to the sequence then it is discarded.</span></span><br><span class="line">v &lt;- 3.8:<span class="number">11.4</span></span><br><span class="line">print(v)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>]  <span class="number">5</span>  <span class="number">6</span>  <span class="number">7</span>  <span class="number">8</span>  <span class="number">9</span> <span class="number">10</span> <span class="number">11</span> <span class="number">12</span> <span class="number">13</span></span><br><span class="line">[<span class="number">1</span>]  <span class="number">6.6</span>  <span class="number">7.6</span>  <span class="number">8.6</span>  <span class="number">9.6</span> <span class="number">10.6</span> <span class="number">11.6</span> <span class="number">12.6</span></span><br><span class="line">[<span class="number">1</span>]  <span class="number">3.8</span>  <span class="number">4.8</span>  <span class="number">5.8</span>  <span class="number">6.8</span>  <span class="number">7.8</span>  <span class="number">8.8</span>  <span class="number">9.8</span> <span class="number">10.8</span></span><br></pre></td></tr></table></figure></p><p><strong>使用 <code>sequence (Seq.)</code> 序列运算符</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create vector with elements from 5 to 9 incrementing by 0.4.</span></span><br><span class="line">print(seq(<span class="number">5</span>, <span class="number">9</span>, by = <span class="number">0.4</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">5.0</span> <span class="number">5.4</span> <span class="number">5.8</span> <span class="number">6.2</span> <span class="number">6.6</span> <span class="number">7.0</span> <span class="number">7.4</span> <span class="number">7.8</span> <span class="number">8.2</span> <span class="number">8.6</span> <span class="number">9.0</span></span><br></pre></td></tr></table></figure></p><p><strong>使用 <code>C()</code> 函数</strong></p><p>如果其中一个元素是字符，则非字符值被强制转换为字符类型。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># The logical and numeric values are converted to characters.</span></span><br><span class="line">s &lt;- <span class="built_in">c</span>(<span class="string">&#x27;apple&#x27;</span>, <span class="string">&#x27;red&#x27;</span>, <span class="number">5</span>, <span class="literal">TRUE</span>)</span><br><span class="line">print(s)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;apple&quot;</span> <span class="string">&quot;red&quot;</span>  <span class="string">&quot;5&quot;</span>  <span class="string">&quot;TRUE&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="访问向量元素"><a href="#访问向量元素" class="headerlink" title="访问向量元素"></a>访问向量元素</h3><p>使用索引访问向量的元素。<code>[]</code> 括号用于建立索引。索引从位置 <code>1</code> 开始。在索引中给出负值会丢弃，<code>TRUE</code>，<code>FALSE</code> 或 <code>0</code> 和 <code>1</code> 的元素，也可用于索引。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Accessing vector elements using position.</span></span><br><span class="line">t &lt;- <span class="built_in">c</span>(<span class="string">&quot;Sun&quot;</span>, <span class="string">&quot;Mon&quot;</span>, <span class="string">&quot;Tue&quot;</span>, <span class="string">&quot;Wed&quot;</span>, <span class="string">&quot;Thurs&quot;</span>, <span class="string">&quot;Fri&quot;</span>, <span class="string">&quot;Sat&quot;</span>)</span><br><span class="line">u &lt;- t[<span class="built_in">c</span>(<span class="number">2</span>, <span class="number">3</span>, <span class="number">6</span>)]</span><br><span class="line">print(u)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Accessing vector elements using logical indexing.</span></span><br><span class="line">v &lt;- t[<span class="built_in">c</span>(<span class="literal">TRUE</span>, <span class="literal">FALSE</span>, <span class="literal">FALSE</span>, <span class="literal">FALSE</span>, <span class="literal">FALSE</span>, <span class="literal">TRUE</span>, <span class="literal">FALSE</span>)]</span><br><span class="line">print(v)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Accessing vector elements using negative indexing.</span></span><br><span class="line">x &lt;- t[<span class="built_in">c</span>(-<span class="number">2</span>, -<span class="number">5</span>)]</span><br><span class="line">print(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Accessing vector elements using 0/1 indexing.</span></span><br><span class="line">y &lt;- t[<span class="built_in">c</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>)]</span><br><span class="line">print(y)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Mon&quot;</span> <span class="string">&quot;Tue&quot;</span> <span class="string">&quot;Fri&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Sun&quot;</span> <span class="string">&quot;Fri&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Sun&quot;</span> <span class="string">&quot;Tue&quot;</span> <span class="string">&quot;Wed&quot;</span> <span class="string">&quot;Fri&quot;</span> <span class="string">&quot;Sat&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Sun&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="向量操作"><a href="#向量操作" class="headerlink" title="向量操作"></a>向量操作</h3><h4 id="向量运算"><a href="#向量运算" class="headerlink" title="向量运算"></a>向量运算</h4><p>可以加，减，乘，除两个相同长度的向量，将结果作为向量输出。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create two vectors.</span></span><br><span class="line">v1 &lt;- <span class="built_in">c</span>(<span class="number">3</span>, <span class="number">8</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">0</span>, <span class="number">11</span>)</span><br><span class="line">v2 &lt;- <span class="built_in">c</span>(<span class="number">4</span>, <span class="number">11</span>, <span class="number">0</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Vector addition.</span></span><br><span class="line">add.result &lt;- v1 + v2</span><br><span class="line">print(add.result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Vector substraction.</span></span><br><span class="line">sub.result &lt;- v1 - v2</span><br><span class="line">print(sub.result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Vector multiplication.</span></span><br><span class="line">multi.result &lt;- v1 * v2</span><br><span class="line">print(multi.result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Vector division.</span></span><br><span class="line">divi.result &lt;- v1 / v2</span><br><span class="line">print(divi.result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>]  <span class="number">7</span> <span class="number">19</span>  <span class="number">4</span> <span class="number">13</span>  <span class="number">1</span> <span class="number">13</span></span><br><span class="line">[<span class="number">1</span>] -<span class="number">1</span> -<span class="number">3</span>  <span class="number">4</span> -<span class="number">3</span> -<span class="number">1</span>  <span class="number">9</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">12</span> <span class="number">88</span>  <span class="number">0</span> <span class="number">40</span>  <span class="number">0</span> <span class="number">22</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">0.7500000</span> <span class="number">0.7272727</span>       <span class="literal">Inf</span> <span class="number">0.6250000</span> <span class="number">0.0000000</span> <span class="number">5.5000000</span></span><br></pre></td></tr></table></figure></p><h4 id="向量元素回收"><a href="#向量元素回收" class="headerlink" title="向量元素回收"></a>向量元素回收</h4><p>如果我们对不等长的两个向量应用算术运算，则较短向量的元素被循环以完成操作。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">v1 &lt;- <span class="built_in">c</span>(<span class="number">3</span>, <span class="number">8</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">0</span>, <span class="number">11</span>)</span><br><span class="line">v2 &lt;- <span class="built_in">c</span>(<span class="number">4</span>, <span class="number">11</span>)</span><br><span class="line"><span class="comment"># V2 becomes c(4, 11, 4, 11, 4, 11)</span></span><br><span class="line"></span><br><span class="line">add.result &lt;- v1 + v2</span><br><span class="line">print(add.result)</span><br><span class="line"></span><br><span class="line">sub.result &lt;- v1 - v2</span><br><span class="line">print(sub.result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>]  <span class="number">7</span> <span class="number">19</span>  <span class="number">8</span> <span class="number">16</span>  <span class="number">4</span> <span class="number">22</span></span><br><span class="line">[<span class="number">1</span>] -<span class="number">1</span> -<span class="number">3</span>  <span class="number">0</span> -<span class="number">6</span> -<span class="number">4</span>  <span class="number">0</span></span><br></pre></td></tr></table></figure></p><h4 id="向量元素排序"><a href="#向量元素排序" class="headerlink" title="向量元素排序"></a>向量元素排序</h4><p>向量中的元素可以使用 <code>sort()</code> 函数排序。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">v &lt;- <span class="built_in">c</span>(<span class="number">3</span>, <span class="number">8</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">0</span>, <span class="number">11</span>, -<span class="number">9</span>, <span class="number">304</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Sort the elements of the vector.</span></span><br><span class="line">sort.result &lt;- sort(v)</span><br><span class="line">print(sort.result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Sort the elements in the reverse order.</span></span><br><span class="line">revsort.result &lt;- sort(v, decreasing = <span class="literal">TRUE</span>)</span><br><span class="line">print(revsort.result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Sorting character vectors.</span></span><br><span class="line">v &lt;- <span class="built_in">c</span>(<span class="string">&quot;Red&quot;</span>, <span class="string">&quot;Blue&quot;</span>, <span class="string">&quot;yellow&quot;</span>, <span class="string">&quot;violet&quot;</span>)</span><br><span class="line">sort.result &lt;- sort(v)</span><br><span class="line">print(sort.result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Sorting character vectors in reverse order.</span></span><br><span class="line">revsort.result &lt;- sort(v, decreasing = <span class="literal">TRUE</span>)</span><br><span class="line">print(revsort.result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>]  -<span class="number">9</span>   <span class="number">0</span>   <span class="number">3</span>   <span class="number">4</span>   <span class="number">5</span>   <span class="number">8</span>  <span class="number">11</span> <span class="number">304</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">304</span>  <span class="number">11</span>   <span class="number">8</span>   <span class="number">5</span>   <span class="number">4</span>   <span class="number">3</span>   <span class="number">0</span>  -<span class="number">9</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Blue&quot;</span>   <span class="string">&quot;Red&quot;</span>    <span class="string">&quot;violet&quot;</span> <span class="string">&quot;yellow&quot;</span></span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;yellow&quot;</span> <span class="string">&quot;violet&quot;</span> <span class="string">&quot;Red&quot;</span>    <span class="string">&quot;Blue&quot;</span> </span><br></pre></td></tr></table></figure></p><h2 id="列表"><a href="#列表" class="headerlink" title="列表"></a>列表</h2><p>列表是 R 语言对象，它包含不同类型的元素，如数字，字符串，向量和其中的另一个列表。列表还可以包含矩阵或函数作为其元素。列表是使用 <code>list()</code> 函数创建的。</p><h3 id="创建列表"><a href="#创建列表" class="headerlink" title="创建列表"></a>创建列表</h3><p>以下是创建包含字符串，数字，向量和逻辑值的列表的示例</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a list containing strings, numbers, vectors and a logical values.</span></span><br><span class="line">list_data &lt;- <span class="built_in">list</span>(<span class="string">&quot;Red&quot;</span>, <span class="string">&quot;Green&quot;</span>, <span class="built_in">c</span>(<span class="number">21</span>, <span class="number">32</span>, <span class="number">11</span>), <span class="literal">TRUE</span>, <span class="number">51.23</span>, <span class="number">119.1</span>)</span><br><span class="line">print(list_data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">1</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Red&quot;</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">2</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Green&quot;</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">3</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="number">21</span> <span class="number">32</span> <span class="number">11</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">4</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="literal">TRUE</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">5</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="number">51.23</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">6</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="number">119.1</span></span><br></pre></td></tr></table></figure></p><h3 id="命名列表元素"><a href="#命名列表元素" class="headerlink" title="命名列表元素"></a>命名列表元素</h3><p>列表元素可以给出名称，并且可以使用这些名称访问它们。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a list containing a vector, a matrix and a list.</span></span><br><span class="line">list_data &lt;- <span class="built_in">list</span>(<span class="built_in">c</span>(<span class="string">&quot;Jan&quot;</span>, <span class="string">&quot;Feb&quot;</span>, <span class="string">&quot;Mar&quot;</span>), matrix(<span class="built_in">c</span>(<span class="number">3</span>, <span class="number">9</span>, <span class="number">5</span>, <span class="number">1</span>, -<span class="number">2</span>, <span class="number">8</span>), nrow = <span class="number">2</span>), <span class="built_in">list</span>(<span class="string">&quot;green&quot;</span>, <span class="number">12.3</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give names to the elements in the list.</span></span><br><span class="line"><span class="built_in">names</span>(list_data) &lt;- <span class="built_in">c</span>(<span class="string">&quot;1st Quarter&quot;</span>, <span class="string">&quot;A_Matrix&quot;</span>, <span class="string">&quot;A Inner list&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Show the list.</span></span><br><span class="line">print(list_data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">$`<span class="number">1</span>st_Quarter`</span><br><span class="line">[1] &quot;Jan&quot; &quot;Feb&quot; &quot;Mar&quot;</span><br><span class="line"></span><br><span class="line">$A_Matrix</span><br><span class="line">     [,1] [,2] [,3]</span><br><span class="line">[1,]    3    5   -2</span><br><span class="line">[2,]    9    1    8</span><br><span class="line"></span><br><span class="line">$A_Inner_list</span><br><span class="line">$A_Inner_list[[1]]</span><br><span class="line">[1] &quot;green&quot;</span><br><span class="line"></span><br><span class="line">$A_Inner_list[[2]]</span><br><span class="line">[1] 12.3</span><br></pre></td></tr></table></figure></p><h3 id="访问列表元素"><a href="#访问列表元素" class="headerlink" title="访问列表元素"></a>访问列表元素</h3><p>列表的元素可以通过列表中元素的索引访问。在命名列表的情况下，它也可以使用名称来访问。</p><p>我们继续使用在上面的例子：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a list containing a vector, a matrix and a list.</span></span><br><span class="line">list_data &lt;- <span class="built_in">list</span>(<span class="built_in">c</span>(<span class="string">&quot;Jan&quot;</span>, <span class="string">&quot;Feb&quot;</span>, <span class="string">&quot;Mar&quot;</span>), matrix(<span class="built_in">c</span>(<span class="number">3</span>, <span class="number">9</span>, <span class="number">5</span>, <span class="number">1</span>, -<span class="number">2</span>, <span class="number">8</span>), nrow = <span class="number">2</span>), <span class="built_in">list</span>(<span class="string">&quot;green&quot;</span>, <span class="number">12.3</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give names to the elements in the list.</span></span><br><span class="line"><span class="built_in">names</span>(list_data) &lt;- <span class="built_in">c</span>(<span class="string">&quot;1st Quarter&quot;</span>, <span class="string">&quot;A_Matrix&quot;</span>, <span class="string">&quot;A Inner list&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Access the first element of the list.</span></span><br><span class="line">print(list_data[<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Access the thrid element. As it is also a list, all its elements will be printed.</span></span><br><span class="line">print(list_data[<span class="number">3</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Access the list element using the name of the element.</span></span><br><span class="line">print(list_data$A_Matrix)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">$`<span class="number">1</span>st_Quarter`</span><br><span class="line">[1] &quot;Jan&quot; &quot;Feb&quot; &quot;Mar&quot;</span><br><span class="line"></span><br><span class="line">$A_Inner_list</span><br><span class="line">$A_Inner_list[[1]]</span><br><span class="line">[1] &quot;green&quot;</span><br><span class="line"></span><br><span class="line">$A_Inner_list[[2]]</span><br><span class="line">[1] 12.3</span><br><span class="line"></span><br><span class="line">     [,1] [,2] [,3]</span><br><span class="line">[1,]    3    5   -2</span><br><span class="line">[2,]    9    1    8</span><br></pre></td></tr></table></figure></p><h3 id="操作列表元素"><a href="#操作列表元素" class="headerlink" title="操作列表元素"></a>操作列表元素</h3><p>我们可以添加，删除和更新列表元素，如下所示。我们只能在列表的末尾添加和删除元素。但我们可以更新任何元素。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a list containing a vector, a matrix and a list.</span></span><br><span class="line">list_data &lt;- <span class="built_in">list</span>(<span class="built_in">c</span>(<span class="string">&quot;Jan&quot;</span>, <span class="string">&quot;Feb&quot;</span>, <span class="string">&quot;Mar&quot;</span>), matrix(<span class="built_in">c</span>(<span class="number">3</span>, <span class="number">9</span>, <span class="number">5</span>, <span class="number">1</span>, -<span class="number">2</span>, <span class="number">8</span>), nrow = <span class="number">2</span>), <span class="built_in">list</span>(<span class="string">&quot;green&quot;</span>, <span class="number">12.3</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Give names to the elements in the list.</span></span><br><span class="line"><span class="built_in">names</span>(list_data) &lt;- <span class="built_in">c</span>(<span class="string">&quot;1st Quarter&quot;</span>, <span class="string">&quot;A_Matrix&quot;</span>, <span class="string">&quot;A Inner list&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Add element at the end of the list.</span></span><br><span class="line">list_data[<span class="number">4</span>] &lt;- <span class="string">&quot;New element&quot;</span></span><br><span class="line">print(list_data[<span class="number">4</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Remove the last element.</span></span><br><span class="line">list_data[<span class="number">4</span>] &lt;- <span class="literal">NULL</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the 4th Element.</span></span><br><span class="line">print(list_data[<span class="number">4</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Update the 3rd Element.</span></span><br><span class="line">list_data[<span class="number">3</span>] &lt;- <span class="string">&quot;updated element&quot;</span></span><br><span class="line">print(list_data[<span class="number">3</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">1</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;New element&quot;</span></span><br><span class="line"></span><br><span class="line">$</span><br><span class="line"><span class="literal">NULL</span></span><br><span class="line"></span><br><span class="line">$`A Inner list`</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;updated element&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="合并列表"><a href="#合并列表" class="headerlink" title="合并列表"></a>合并列表</h3><p>通过将所有列表放在一个 <code>list()</code> 函数中，您可以将许多列表合并到一个列表中。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create two lists.</span></span><br><span class="line">list1 &lt;- <span class="built_in">list</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line">list2 &lt;- <span class="built_in">list</span>(<span class="string">&quot;Sun&quot;</span>, <span class="string">&quot;Mon&quot;</span>, <span class="string">&quot;Tue&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Merge the two lists.</span></span><br><span class="line">merged.list &lt;- <span class="built_in">c</span>(list1, list2)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the merged list.</span></span><br><span class="line">print(merged.list)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">1</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="number">1</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">2</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="number">2</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">3</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="number">3</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">4</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Sun&quot;</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">5</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Mon&quot;</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">6</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="string">&quot;Tue&quot;</span></span><br></pre></td></tr></table></figure></p><h3 id="将列表转换为向量"><a href="#将列表转换为向量" class="headerlink" title="将列表转换为向量"></a>将列表转换为向量</h3><p>列表可以转换为向量，使得向量的元素可以用于进一步的操作。可以在将列表转换为向量之后应用对向量的所有算术运算。我们可以使用 <code>unlist()</code> 函数完成这个转换。它将列表作为输入并生成向量。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create lists.</span></span><br><span class="line">list1 &lt;- <span class="built_in">list</span>(<span class="number">1</span>:<span class="number">5</span>)</span><br><span class="line">print(list1)</span><br><span class="line"></span><br><span class="line">list2 &lt;-<span class="built_in">list</span>(<span class="number">10</span>:<span class="number">14</span>)</span><br><span class="line">print(list2)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Convert the lists to vectors.</span></span><br><span class="line">v1 &lt;- unlist(list1)</span><br><span class="line">v2 &lt;- unlist(list2)</span><br><span class="line"></span><br><span class="line">print(v1)</span><br><span class="line">print(v2)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Now add the vectors</span></span><br><span class="line">result &lt;- v1 + v2</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">1</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span></span><br><span class="line"></span><br><span class="line">[[<span class="number">1</span>]]</span><br><span class="line">[<span class="number">1</span>] <span class="number">10</span> <span class="number">11</span> <span class="number">12</span> <span class="number">13</span> <span class="number">14</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">10</span> <span class="number">11</span> <span class="number">12</span> <span class="number">13</span> <span class="number">14</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">11</span> <span class="number">13</span> <span class="number">15</span> <span class="number">17</span> <span class="number">19</span></span><br></pre></td></tr></table></figure></p><h2 id="矩阵"><a href="#矩阵" class="headerlink" title="矩阵"></a>矩阵</h2><p>矩阵是其中元素以二维矩形布局布置的 R 对象。它们包含相同原子类型的元素。虽然我们可以创建一个只包含字符或只包含逻辑值的矩阵，但它们没有太多用处。我们使用包含数字元素的矩阵用于数学计算。</p><h3 id="创建矩阵"><a href="#创建矩阵" class="headerlink" title="创建矩阵"></a>创建矩阵</h3><p>R 语言中使用 <code>matrix()</code> 函数创建一个矩阵。</p><p><strong>语法</strong></p><p>在 R 语言中创建矩阵的基本语法是：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">matrix(data, nrow, ncol, byrow, <span class="built_in">dimnames</span>)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>data</code> 是成为矩阵的数据元素的输入向量。</li><li><code>nrow</code> 是要创建的行数。</li><li><code>ncol</code> 是要创建的列数。</li><li><code>byrow</code> 是一个逻辑线索。如果为 <code>TRUE</code>，则输入向量元素按行排列。</li><li><code>dimname</code> 是分配给行和列的名称。</li></ul><p><strong>实例</strong></p><p>创建一个以数字向量作为输入的矩阵</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Elements are arranged sequentially by row.</span></span><br><span class="line">M &lt;- matrix(<span class="built_in">c</span>(<span class="number">3</span>:<span class="number">14</span>), nrow = <span class="number">4</span>, byrow = <span class="literal">TRUE</span>)</span><br><span class="line">print(M)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Elements are arranged sequentially by column.</span></span><br><span class="line">N &lt;- matrix(<span class="built_in">c</span>(<span class="number">3</span>:<span class="number">14</span>), nrow = <span class="number">4</span>, byrow = <span class="literal">FALSE</span>)</span><br><span class="line">print(N)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Define the column and row names.</span></span><br><span class="line">rownames = <span class="built_in">c</span>(<span class="string">&quot;row1&quot;</span>, <span class="string">&quot;row2&quot;</span>, <span class="string">&quot;row3&quot;</span>, <span class="string">&quot;row4&quot;</span>)</span><br><span class="line">colnames = <span class="built_in">c</span>(<span class="string">&quot;col1&quot;</span>, <span class="string">&quot;col2&quot;</span>, <span class="string">&quot;col3&quot;</span>)</span><br><span class="line"></span><br><span class="line">P &lt;- matrix(<span class="built_in">c</span>(<span class="number">3</span>:<span class="number">14</span>), nrow = <span class="number">4</span>, byrow = <span class="literal">TRUE</span>, <span class="built_in">dimnames</span> = <span class="built_in">list</span>(rownames, colnames))</span><br><span class="line">print(P)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">3</span>    <span class="number">4</span>    <span class="number">5</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">6</span>    <span class="number">7</span>    <span class="number">8</span></span><br><span class="line">[<span class="number">3</span>,]    <span class="number">9</span>   <span class="number">10</span>   <span class="number">11</span></span><br><span class="line">[<span class="number">4</span>,]   <span class="number">12</span>   <span class="number">13</span>   <span class="number">14</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">3</span>    <span class="number">7</span>   <span class="number">11</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">4</span>    <span class="number">8</span>   <span class="number">12</span></span><br><span class="line">[<span class="number">3</span>,]    <span class="number">5</span>    <span class="number">9</span>   <span class="number">13</span></span><br><span class="line">[<span class="number">4</span>,]    <span class="number">6</span>   <span class="number">10</span>   <span class="number">14</span></span><br><span class="line"></span><br><span class="line">     col1 col2 col3</span><br><span class="line">row1    <span class="number">3</span>    <span class="number">4</span>    <span class="number">5</span></span><br><span class="line">row2    <span class="number">6</span>    <span class="number">7</span>    <span class="number">8</span></span><br><span class="line">row3    <span class="number">9</span>   <span class="number">10</span>   <span class="number">11</span></span><br><span class="line">row4   <span class="number">12</span>   <span class="number">13</span>   <span class="number">14</span></span><br></pre></td></tr></table></figure></p><h3 id="访问矩阵的元素"><a href="#访问矩阵的元素" class="headerlink" title="访问矩阵的元素"></a>访问矩阵的元素</h3><p>可以通过使用元素的列和行索引来访问矩阵的元素。我们考虑上面的矩阵 <code>P</code> 找到下面的具体元素。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Define the column and row names.</span></span><br><span class="line">rownames = <span class="built_in">c</span>(<span class="string">&quot;row1&quot;</span>, <span class="string">&quot;row2&quot;</span>, <span class="string">&quot;row3&quot;</span>, <span class="string">&quot;row4&quot;</span>)</span><br><span class="line">colnames = <span class="built_in">c</span>(<span class="string">&quot;col1&quot;</span>, <span class="string">&quot;col2&quot;</span>, <span class="string">&quot;col3&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the matrix.</span></span><br><span class="line">P &lt;- matrix(<span class="built_in">c</span>(<span class="number">3</span>:<span class="number">14</span>), nrow = <span class="number">4</span>, byrow = <span class="literal">TRUE</span>, <span class="built_in">dimnames</span> = <span class="built_in">list</span>(rownames, colnames))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Access the element at 3rd column and 1st row.</span></span><br><span class="line">print(P[<span class="number">1</span>, <span class="number">3</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Access the element at 2nd column and 4th row.</span></span><br><span class="line">print(P[<span class="number">4</span>, <span class="number">2</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Access only the  2nd row.</span></span><br><span class="line">print(P[<span class="number">2</span>, ])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Access only the 3rd column.</span></span><br><span class="line">print(P[, <span class="number">3</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">5</span></span><br><span class="line">[<span class="number">1</span>] <span class="number">13</span></span><br><span class="line">col1 col2 col3 </span><br><span class="line">   <span class="number">6</span>    <span class="number">7</span>    <span class="number">8</span> </span><br><span class="line">row1 row2 row3 row4 </span><br><span class="line">   <span class="number">5</span>    <span class="number">8</span>   <span class="number">11</span>   <span class="number">14</span> </span><br></pre></td></tr></table></figure></p><h3 id="矩阵计算"><a href="#矩阵计算" class="headerlink" title="矩阵计算"></a>矩阵计算</h3><p>使用 R 运算符对矩阵执行各种数学运算。操作的结果也是一个矩阵。</p><p>对于操作中涉及的矩阵，维度（行数和列数）应该相同。</p><h4 id="加法和减法"><a href="#加法和减法" class="headerlink" title="加法和减法"></a>加法和减法</h4><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create two 2x3 matrices.</span></span><br><span class="line">matrix1 &lt;- matrix(<span class="built_in">c</span>(<span class="number">3</span>, <span class="number">9</span>, -<span class="number">1</span>, <span class="number">4</span>, <span class="number">2</span>, <span class="number">6</span>), nrow = <span class="number">2</span>)</span><br><span class="line">print(matrix1)</span><br><span class="line"></span><br><span class="line">matrix2 &lt;- matrix(<span class="built_in">c</span>(<span class="number">5</span>, <span class="number">2</span>, <span class="number">0</span>, <span class="number">9</span>, <span class="number">3</span>, <span class="number">4</span>), nrow = <span class="number">2</span>)</span><br><span class="line">print(matrix2)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Add the matrices.</span></span><br><span class="line">result &lt;- matrix1 + matrix2</span><br><span class="line">cat(<span class="string">&quot;Result of addition&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Subtract the matrices</span></span><br><span class="line">result &lt;- matrix1 - matrix2</span><br><span class="line">cat(<span class="string">&quot;Result of subtraction&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">3</span>   -<span class="number">1</span>    <span class="number">2</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">9</span>    <span class="number">4</span>    <span class="number">6</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">5</span>    <span class="number">0</span>    <span class="number">3</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">2</span>    <span class="number">9</span>    <span class="number">4</span></span><br><span class="line"></span><br><span class="line">Result of addition</span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">8</span>   -<span class="number">1</span>    <span class="number">5</span></span><br><span class="line">[<span class="number">2</span>,]   <span class="number">11</span>   <span class="number">13</span>   <span class="number">10</span></span><br><span class="line"></span><br><span class="line">Result of subtraction</span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]   -<span class="number">2</span>   -<span class="number">1</span>   -<span class="number">1</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">7</span>   -<span class="number">5</span>    <span class="number">2</span></span><br></pre></td></tr></table></figure></p><h4 id="乘法和除法"><a href="#乘法和除法" class="headerlink" title="乘法和除法"></a>乘法和除法</h4><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create two 2x3 matrices.</span></span><br><span class="line">matrix1 &lt;- matrix(<span class="built_in">c</span>(<span class="number">3</span>, <span class="number">9</span>, -<span class="number">1</span>, <span class="number">4</span>, <span class="number">2</span>, <span class="number">6</span>), nrow = <span class="number">2</span>)</span><br><span class="line">print(matrix1)</span><br><span class="line"></span><br><span class="line">matrix2 &lt;- matrix(<span class="built_in">c</span>(<span class="number">5</span>, <span class="number">2</span>, <span class="number">0</span>, <span class="number">9</span>, <span class="number">3</span>, <span class="number">4</span>), nrow = <span class="number">2</span>)</span><br><span class="line">print(matrix2)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Multiply the matrices.</span></span><br><span class="line">result &lt;- matrix1 * matrix2</span><br><span class="line">cat(<span class="string">&quot;Result of multiplication&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Divide the matrices</span></span><br><span class="line">result &lt;- matrix1 / matrix2</span><br><span class="line">cat(<span class="string">&quot;Result of division&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">3</span>   -<span class="number">1</span>    <span class="number">2</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">9</span>    <span class="number">4</span>    <span class="number">6</span></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">5</span>    <span class="number">0</span>    <span class="number">3</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">2</span>    <span class="number">9</span>    <span class="number">4</span></span><br><span class="line">Result of multiplication </span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]   <span class="number">15</span>    <span class="number">0</span>    <span class="number">6</span></span><br><span class="line">[<span class="number">2</span>,]   <span class="number">18</span>   <span class="number">36</span>   <span class="number">24</span></span><br><span class="line">Result of division </span><br><span class="line">     [,<span class="number">1</span>]      [,<span class="number">2</span>]      [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]  <span class="number">0.6</span>      -<span class="literal">Inf</span> <span class="number">0.6666667</span></span><br><span class="line">[<span class="number">2</span>,]  <span class="number">4.5</span> <span class="number">0.4444444</span> <span class="number">1.5000000</span></span><br></pre></td></tr></table></figure></p><h2 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h2><p>数组是可以在两个以上维度中存储数据的 R 数据对象。</p><p>例如：如果我们创建一个维度 <code>(2，3，4)</code> 的数组，则它创建 <code>4</code> 个矩形矩阵，每个矩阵具有 <code>2</code> 行和 <code>3</code> 列。数组只能存储数据类型。</p><h3 id="创建数组"><a href="#创建数组" class="headerlink" title="创建数组"></a>创建数组</h3><p>使用 <code>array()</code> 函数创建数组。它使用向量作为输入，并使用 <code>dim</code> 参数中的值创建数组。</p><p><strong>实例</strong></p><p>以下示例创建一个由两个 <code>3x3</code> 矩阵组成的数组，每个矩阵具有 <code>3</code> 行和 <code>3</code> 列。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create two vectors of different lengths.</span></span><br><span class="line">vector1 &lt;- <span class="built_in">c</span>(<span class="number">5</span>, <span class="number">9</span>, <span class="number">3</span>)</span><br><span class="line">vector2 &lt;- <span class="built_in">c</span>(<span class="number">10</span>, <span class="number">11</span>, <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Take these vectors as input to the array.</span></span><br><span class="line">result &lt;- array(<span class="built_in">c</span>(vector1, vector2), <span class="built_in">dim</span> = <span class="built_in">c</span>(<span class="number">3</span>, <span class="number">3</span>, <span class="number">2</span>))</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">, , <span class="number">1</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">5</span>   <span class="number">10</span>   <span class="number">13</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">9</span>   <span class="number">11</span>   <span class="number">14</span></span><br><span class="line">[<span class="number">3</span>,]    <span class="number">3</span>   <span class="number">12</span>   <span class="number">15</span></span><br><span class="line"></span><br><span class="line">, , <span class="number">2</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">5</span>   <span class="number">10</span>   <span class="number">13</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">9</span>   <span class="number">11</span>   <span class="number">14</span></span><br><span class="line">[<span class="number">3</span>,]    <span class="number">3</span>   <span class="number">12</span>   <span class="number">15</span></span><br></pre></td></tr></table></figure></p><h3 id="命名列和行"><a href="#命名列和行" class="headerlink" title="命名列和行"></a>命名列和行</h3><p>我们可以使用 <code>dimnames</code> 参数给数组中的行，列和矩阵命名。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create two vectors of different lengths.</span></span><br><span class="line">vector1 &lt;- <span class="built_in">c</span>(<span class="number">5</span>, <span class="number">9</span>, <span class="number">3</span>)</span><br><span class="line">vector2 &lt;- <span class="built_in">c</span>(<span class="number">10</span>, <span class="number">11</span>, <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>)</span><br><span class="line">column.names &lt;- <span class="built_in">c</span>(<span class="string">&quot;COL1&quot;</span>, <span class="string">&quot;COL2&quot;</span>, <span class="string">&quot;COL3&quot;</span>)</span><br><span class="line">row.names &lt;- <span class="built_in">c</span>(<span class="string">&quot;ROW1&quot;</span>, <span class="string">&quot;ROW2&quot;</span>, <span class="string">&quot;ROW3&quot;</span>)</span><br><span class="line">matrix.names &lt;- <span class="built_in">c</span>(<span class="string">&quot;Matrix1&quot;</span>, <span class="string">&quot;Matrix2&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Take these vectors as input to the array.</span></span><br><span class="line">result &lt;- array(<span class="built_in">c</span>(vector1, vector2), <span class="built_in">dim</span> = <span class="built_in">c</span>(<span class="number">3</span>, <span class="number">3</span>, <span class="number">2</span>), <span class="built_in">dimnames</span> = <span class="built_in">list</span>(row.names, column.names, matrix.names))</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">, , Matrix1</span><br><span class="line"></span><br><span class="line">     COL1 COL2 COL3</span><br><span class="line">ROW1    <span class="number">5</span>   <span class="number">10</span>   <span class="number">13</span></span><br><span class="line">ROW2    <span class="number">9</span>   <span class="number">11</span>   <span class="number">14</span></span><br><span class="line">ROW3    <span class="number">3</span>   <span class="number">12</span>   <span class="number">15</span></span><br><span class="line"></span><br><span class="line">, , Matrix2</span><br><span class="line"></span><br><span class="line">     COL1 COL2 COL3</span><br><span class="line">ROW1    <span class="number">5</span>   <span class="number">10</span>   <span class="number">13</span></span><br><span class="line">ROW2    <span class="number">9</span>   <span class="number">11</span>   <span class="number">14</span></span><br><span class="line">ROW3    <span class="number">3</span>   <span class="number">12</span>   <span class="number">15</span></span><br></pre></td></tr></table></figure></p><h3 id="访问数组元素"><a href="#访问数组元素" class="headerlink" title="访问数组元素"></a>访问数组元素</h3><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create two vectors of different lengths.</span></span><br><span class="line">vector1 &lt;- <span class="built_in">c</span>(<span class="number">5</span>, <span class="number">9</span>, <span class="number">3</span>)</span><br><span class="line">vector2 &lt;- <span class="built_in">c</span>(<span class="number">10</span>, <span class="number">11</span>, <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>)</span><br><span class="line">column.names &lt;- <span class="built_in">c</span>(<span class="string">&quot;COL1&quot;</span>, <span class="string">&quot;COL2&quot;</span>, <span class="string">&quot;COL3&quot;</span>)</span><br><span class="line">row.names &lt;- <span class="built_in">c</span>(<span class="string">&quot;ROW1&quot;</span>, <span class="string">&quot;ROW2&quot;</span>, <span class="string">&quot;ROW3&quot;</span>)</span><br><span class="line">matrix.names &lt;- <span class="built_in">c</span>(<span class="string">&quot;Matrix1&quot;</span>, <span class="string">&quot;Matrix2&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Take these vectors as input to the array.</span></span><br><span class="line">result &lt;- array(<span class="built_in">c</span>(vector1, vector2), <span class="built_in">dim</span> = <span class="built_in">c</span>(<span class="number">3</span>, <span class="number">3</span>, <span class="number">2</span>), <span class="built_in">dimnames</span> = <span class="built_in">list</span>(row.names, column.names, matrix.names))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the third row of the second matrix of the array.</span></span><br><span class="line">print(result[<span class="number">3</span>, , <span class="number">2</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the element in the 1st row and 3rd column of the 1st matrix.</span></span><br><span class="line">print(result[<span class="number">1</span>, <span class="number">3</span>, <span class="number">1</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the 2nd Matrix.</span></span><br><span class="line">print(result[, , <span class="number">2</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">COL1 COL2 COL3 </span><br><span class="line">   <span class="number">3</span>   <span class="number">12</span>   <span class="number">15</span> </span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">13</span></span><br><span class="line"></span><br><span class="line">     COL1 COL2 COL3</span><br><span class="line">ROW1    <span class="number">5</span>   <span class="number">10</span>   <span class="number">13</span></span><br><span class="line">ROW2    <span class="number">9</span>   <span class="number">11</span>   <span class="number">14</span></span><br><span class="line">ROW3    <span class="number">3</span>   <span class="number">12</span>   <span class="number">15</span></span><br></pre></td></tr></table></figure></p><h3 id="操作数组元素"><a href="#操作数组元素" class="headerlink" title="操作数组元素"></a>操作数组元素</h3><p>由于数组由多维构成矩阵，所以对数组元素的操作通过访问矩阵的元素来执行。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create two vectors of different lengths.</span></span><br><span class="line">vector1 &lt;- <span class="built_in">c</span>(<span class="number">5</span>, <span class="number">9</span>, <span class="number">3</span>)</span><br><span class="line">vector2 &lt;- <span class="built_in">c</span>(<span class="number">10</span>, <span class="number">11</span>, <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Take these vectors as input to the array.</span></span><br><span class="line">array1 &lt;- array(<span class="built_in">c</span>(vector1, vector2), <span class="built_in">dim</span> = <span class="built_in">c</span>(<span class="number">3</span>, <span class="number">3</span>, <span class="number">2</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create two vectors of different lengths.</span></span><br><span class="line">vector3 &lt;- <span class="built_in">c</span>(<span class="number">9</span>, <span class="number">1</span>, <span class="number">0</span>)</span><br><span class="line">vector4 &lt;- <span class="built_in">c</span>(<span class="number">6</span>, <span class="number">0</span>, <span class="number">11</span>, <span class="number">3</span>, <span class="number">14</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">6</span>, <span class="number">9</span>)</span><br><span class="line">array2 &lt;- array(<span class="built_in">c</span>(vector1, vector2), <span class="built_in">dim</span> = <span class="built_in">c</span>(<span class="number">3</span>, <span class="number">3</span>, <span class="number">2</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># create matrices from these arrays.</span></span><br><span class="line">matrix1 &lt;- array1[, , <span class="number">2</span>]</span><br><span class="line">matrix2 &lt;- array2[, , <span class="number">2</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># Add the matrices.</span></span><br><span class="line">result &lt;- matrix1 + matrix2</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]   <span class="number">10</span>   <span class="number">20</span>   <span class="number">26</span></span><br><span class="line">[<span class="number">2</span>,]   <span class="number">18</span>   <span class="number">22</span>   <span class="number">28</span></span><br><span class="line">[<span class="number">3</span>,]    <span class="number">6</span>   <span class="number">24</span>   <span class="number">30</span></span><br></pre></td></tr></table></figure></p><h3 id="跨数组元素的计算"><a href="#跨数组元素的计算" class="headerlink" title="跨数组元素的计算"></a>跨数组元素的计算</h3><p>我们可以使用 <code>apply()</code> 函数在数组中的元素上进行计算。</p><p><strong>语法</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">apply(x, margin, fun)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>x</code> 是一个数组。</li><li><code>margin</code> 是所使用的数据集的名称。</li><li><code>fun</code> 是要应用于数组元素的函数。</li></ul><p><strong>实例</strong></p><p>我们下面使用 <code>apply()</code> 函数计算所有矩阵中数组行中元素的总和。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create two vectors of different lengths.</span></span><br><span class="line">vector1 &lt;- <span class="built_in">c</span>(<span class="number">5</span>, <span class="number">9</span>, <span class="number">3</span>)</span><br><span class="line">vector2 &lt;- <span class="built_in">c</span>(<span class="number">10</span>, <span class="number">11</span>, <span class="number">12</span>, <span class="number">13</span>, <span class="number">14</span>, <span class="number">15</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Take these vectors as input to the array.</span></span><br><span class="line">new.array &lt;- array(<span class="built_in">c</span>(vector1, vector2), <span class="built_in">dim</span> = <span class="built_in">c</span>(<span class="number">3</span>, <span class="number">3</span>, <span class="number">2</span>))</span><br><span class="line">print(new.array)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Use apply to calculate the sum of the rows across all the matrices.</span></span><br><span class="line">result &lt;- apply(new.array, <span class="built_in">c</span>(<span class="number">1</span>), <span class="built_in">sum</span>)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">, , <span class="number">1</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">5</span>   <span class="number">10</span>   <span class="number">13</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">9</span>   <span class="number">11</span>   <span class="number">14</span></span><br><span class="line">[<span class="number">3</span>,]    <span class="number">3</span>   <span class="number">12</span>   <span class="number">15</span></span><br><span class="line"></span><br><span class="line">, , <span class="number">2</span></span><br><span class="line"></span><br><span class="line">     [,<span class="number">1</span>] [,<span class="number">2</span>] [,<span class="number">3</span>]</span><br><span class="line">[<span class="number">1</span>,]    <span class="number">5</span>   <span class="number">10</span>   <span class="number">13</span></span><br><span class="line">[<span class="number">2</span>,]    <span class="number">9</span>   <span class="number">11</span>   <span class="number">14</span></span><br><span class="line">[<span class="number">3</span>,]    <span class="number">3</span>   <span class="number">12</span>   <span class="number">15</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="number">56</span> <span class="number">68</span> <span class="number">60</span></span><br></pre></td></tr></table></figure></p><h2 id="因子"><a href="#因子" class="headerlink" title="因子"></a>因子</h2><p>因子是用于对数据进行分类并将其存储为级别的数据对象。它们可以存储字符串和整数。它们在具有有限数量的唯一值的列中很有用。像 <code>男性</code>，<code>女性</code> 和 <code>True</code>，<code>False</code> 等。它们在统计建模的数据分析中很有用。</p><h3 id="创建因子"><a href="#创建因子" class="headerlink" title="创建因子"></a>创建因子</h3><p>使用 <code>factor()</code> 函数通过将向量作为输入创建因子。</p><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a vector as input.</span></span><br><span class="line">data &lt;- <span class="built_in">c</span>(<span class="string">&quot;East&quot;</span>, <span class="string">&quot;West&quot;</span>, <span class="string">&quot;East&quot;</span>, <span class="string">&quot;North&quot;</span>, <span class="string">&quot;North&quot;</span>, <span class="string">&quot;East&quot;</span>, <span class="string">&quot;West&quot;</span>, <span class="string">&quot;West&quot;</span>, <span class="string">&quot;West&quot;</span>, <span class="string">&quot;East&quot;</span>, <span class="string">&quot;North&quot;</span>)</span><br><span class="line"></span><br><span class="line">print(data)</span><br><span class="line">print(is.factor(data))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Apply the factor function.</span></span><br><span class="line">factor_data &lt;- factor(data)</span><br><span class="line"></span><br><span class="line">print(factor_data)</span><br><span class="line">print(is.factor(factor_data))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line"> [<span class="number">1</span>] <span class="string">&quot;East&quot;</span>  <span class="string">&quot;West&quot;</span>  <span class="string">&quot;East&quot;</span>  <span class="string">&quot;North&quot;</span> <span class="string">&quot;North&quot;</span> <span class="string">&quot;East&quot;</span>  <span class="string">&quot;West&quot;</span>  <span class="string">&quot;West&quot;</span>  <span class="string">&quot;West&quot;</span>  <span class="string">&quot;East&quot;</span> <span class="string">&quot;North&quot;</span></span><br><span class="line"> </span><br><span class="line">[<span class="number">1</span>] <span class="literal">FALSE</span></span><br><span class="line"></span><br><span class="line"> [<span class="number">1</span>] East  West  East  North North East  West  West  West  East  North</span><br><span class="line">Levels: East North West</span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="literal">TRUE</span></span><br></pre></td></tr></table></figure></p><h3 id="数据框的因子"><a href="#数据框的因子" class="headerlink" title="数据框的因子"></a>数据框的因子</h3><p>在创建具有文本数据列的任何数据框时，R 语言将文本列视为分类数据并在其上创建因子。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the vectors for data frame.</span></span><br><span class="line">height &lt;- <span class="built_in">c</span>(<span class="number">132</span>, <span class="number">151</span>, <span class="number">162</span>, <span class="number">139</span>, <span class="number">166</span>, <span class="number">147</span>, <span class="number">122</span>)</span><br><span class="line">weight &lt;- <span class="built_in">c</span>(<span class="number">48</span>, <span class="number">49</span>, <span class="number">66</span>, <span class="number">53</span>, <span class="number">67</span>, <span class="number">52</span>, <span class="number">40</span>)</span><br><span class="line">gender &lt;- <span class="built_in">c</span>(<span class="string">&quot;male&quot;</span>, <span class="string">&quot;male&quot;</span>, <span class="string">&quot;female&quot;</span>, <span class="string">&quot;female&quot;</span>, <span class="string">&quot;male&quot;</span>, <span class="string">&quot;female&quot;</span>, <span class="string">&quot;male&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the data frame.</span></span><br><span class="line">input_data &lt;- data.frame(height, weight, gender)</span><br><span class="line">print(input_data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Test if the gender column is a factor.</span></span><br><span class="line">print(is.factor(input_data$gender))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the gender column so see the levels.</span></span><br><span class="line">print(input_data$gender)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  height weight gender</span><br><span class="line">1    <span class="number">132</span>     <span class="number">48</span>   male</span><br><span class="line">2    <span class="number">151</span>     <span class="number">49</span>   male</span><br><span class="line">3    <span class="number">162</span>     <span class="number">66</span> female</span><br><span class="line">4    <span class="number">139</span>     <span class="number">53</span> female</span><br><span class="line">5    <span class="number">166</span>     <span class="number">67</span>   male</span><br><span class="line">6    <span class="number">147</span>     <span class="number">52</span> female</span><br><span class="line">7    <span class="number">122</span>     <span class="number">40</span>   male</span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] <span class="literal">TRUE</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>] male   male   female female male   female male  </span><br><span class="line">Levels: female male</span><br></pre></td></tr></table></figure></p><h3 id="更改级别顺序"><a href="#更改级别顺序" class="headerlink" title="更改级别顺序"></a>更改级别顺序</h3><p>可以通过使用新的等级次序再次应用因子函数来改变因子中的等级的顺序。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">data &lt;- <span class="built_in">c</span>(<span class="string">&quot;East&quot;</span>, <span class="string">&quot;West&quot;</span>, <span class="string">&quot;East&quot;</span>, <span class="string">&quot;North&quot;</span>, <span class="string">&quot;North&quot;</span>, <span class="string">&quot;East&quot;</span>, <span class="string">&quot;West&quot;</span>, <span class="string">&quot;West&quot;</span>, <span class="string">&quot;West&quot;</span>, <span class="string">&quot;East&quot;</span>, <span class="string">&quot;North&quot;</span>)</span><br><span class="line"><span class="comment"># Create the factors</span></span><br><span class="line">factor_data &lt;- factor(data)</span><br><span class="line">print(factor_data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Apply the factor function with required order of the level.</span></span><br><span class="line">new_order_data &lt;- factor(factor_data, levels = <span class="built_in">c</span>(<span class="string">&quot;East&quot;</span>, <span class="string">&quot;West&quot;</span>, <span class="string">&quot;North&quot;</span>))</span><br><span class="line">print(new_order_data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line"> [<span class="number">1</span>] East  West  East  North North East  West  West  West  East  North</span><br><span class="line">Levels: East North West</span><br><span class="line"></span><br><span class="line"> [<span class="number">1</span>] East  West  East  North North East  West  West  West  East  North</span><br><span class="line">Levels: East West North</span><br></pre></td></tr></table></figure></p><h3 id="生成因子级别"><a href="#生成因子级别" class="headerlink" title="生成因子级别"></a>生成因子级别</h3><p>我们可以使用 <code>gl()</code> 函数生成因子级别。它需要两个整数作为输入，指示每个级别有多少级别和多少次。</p><p><strong>语法</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gl(n, k, labels)</span><br></pre></td></tr></table></figure></p><p>以下是所使用的参数的说明：</p><ul><li><code>n</code> 是给出级数的整数。</li><li><code>k</code> 是给出复制数目的整数。</li><li><code>labels</code> 是所得因子水平的标签向量。</li></ul><p><strong>实例</strong></p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">v &lt;- gl(<span class="number">3</span>, <span class="number">4</span>, labels = <span class="built_in">c</span>(<span class="string">&quot;Tampa&quot;</span>, <span class="string">&quot;Seattle&quot;</span>, <span class="string">&quot;Boston&quot;</span>))</span><br><span class="line">print(v)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line"> [<span class="number">1</span>] Tampa   Tampa   Tampa   Tampa   Seattle Seattle Seattle Seattle Boston  Boston  Boston  Boston </span><br><span class="line">Levels: Tampa Seattle Boston</span><br></pre></td></tr></table></figure></p><h2 id="数据框"><a href="#数据框" class="headerlink" title="数据框"></a>数据框</h2><p>数据框是表或二维阵列状结构，其中每一列包含一个变量的值，并且每一行包含来自每一列的一组值。</p><p>以下是数据框的特性。</p><ul><li>列名称应为非空。</li><li>行名称应该是唯一的。</li><li>存储在数据框中的数据可以是数字，因子或字符类型。</li><li>每个列应包含相同数量的数据项。</li></ul><h3 id="创建数据框"><a href="#创建数据框" class="headerlink" title="创建数据框"></a>创建数据框</h3><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data frame.</span></span><br><span class="line">emp.data &lt;- data.frame(</span><br><span class="line">   emp_id = <span class="built_in">c</span>(<span class="number">1</span>:<span class="number">5</span>), </span><br><span class="line">   emp_name = <span class="built_in">c</span>(<span class="string">&quot;Rick&quot;</span>, <span class="string">&quot;Dan&quot;</span>, <span class="string">&quot;Michelle&quot;</span>, <span class="string">&quot;Ryan&quot;</span>, <span class="string">&quot;Gary&quot;</span>), </span><br><span class="line">   salary = <span class="built_in">c</span>(<span class="number">623.3</span>, <span class="number">515.2</span>, <span class="number">611.0</span>, <span class="number">729.0</span>, <span class="number">843.25</span>), </span><br><span class="line">   start_date = as.Date(<span class="built_in">c</span>(<span class="string">&quot;2012-01-01&quot;</span>, <span class="string">&quot;2013-09-23&quot;</span>, <span class="string">&quot;2014-11-15&quot;</span>, <span class="string">&quot;2014-05-11&quot;</span>, <span class="string">&quot;2015-03-27&quot;</span>)), </span><br><span class="line">   stringsAsFactors = <span class="literal">FALSE</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the data frame.</span></span><br><span class="line">print(emp.data) </span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line"> emp_id    emp_name     salary     start_date</span><br><span class="line">1     <span class="number">1</span>     Rick        <span class="number">623.30</span>     <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span></span><br><span class="line">2     <span class="number">2</span>     Dan         <span class="number">515.20</span>     <span class="number">2013</span>-<span class="number">09</span>-<span class="number">23</span></span><br><span class="line">3     <span class="number">3</span>     Michelle    <span class="number">611.00</span>     <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span></span><br><span class="line">4     <span class="number">4</span>     Ryan        <span class="number">729.00</span>     <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span></span><br><span class="line">5     <span class="number">5</span>     Gary        <span class="number">843.25</span>     <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span></span><br></pre></td></tr></table></figure></p><h3 id="获取数据框的结构"><a href="#获取数据框的结构" class="headerlink" title="获取数据框的结构"></a>获取数据框的结构</h3><p>通过使用 <code>str()</code> 函数可以看到数据框的结构。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data frame.</span></span><br><span class="line">emp.data &lt;- data.frame(</span><br><span class="line">   emp_id = <span class="built_in">c</span>(<span class="number">1</span>:<span class="number">5</span>), </span><br><span class="line">   emp_name = <span class="built_in">c</span>(<span class="string">&quot;Rick&quot;</span>, <span class="string">&quot;Dan&quot;</span>, <span class="string">&quot;Michelle&quot;</span>, <span class="string">&quot;Ryan&quot;</span>, <span class="string">&quot;Gary&quot;</span>), </span><br><span class="line">   salary = <span class="built_in">c</span>(<span class="number">623.3</span>, <span class="number">515.2</span>, <span class="number">611.0</span>, <span class="number">729.0</span>, <span class="number">843.25</span>), </span><br><span class="line">   start_date = as.Date(<span class="built_in">c</span>(<span class="string">&quot;2012-01-01&quot;</span>, <span class="string">&quot;2013-09-23&quot;</span>, <span class="string">&quot;2014-11-15&quot;</span>, <span class="string">&quot;2014-05-11&quot;</span>, <span class="string">&quot;2015-03-27&quot;</span>)), </span><br><span class="line">   stringsAsFactors = <span class="literal">FALSE</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get the structure of the data frame.</span></span><br><span class="line">str(emp.data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line"><span class="string">&#x27;data.frame&#x27;</span>:<span class="number">5</span> obs. of  <span class="number">4</span> variables:</span><br><span class="line"> $ emp_id    : int  <span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span></span><br><span class="line"> $ emp_name  : chr  <span class="string">&quot;Rick&quot;</span> <span class="string">&quot;Dan&quot;</span> <span class="string">&quot;Michelle&quot;</span> <span class="string">&quot;Ryan&quot;</span> ...</span><br><span class="line"> $ salary    : num  <span class="number">623</span> <span class="number">515</span> <span class="number">611</span> <span class="number">729</span> <span class="number">843</span></span><br><span class="line"> $ start_date: Date, format: <span class="string">&quot;2012-01-01&quot;</span> <span class="string">&quot;2013-09-23&quot;</span> <span class="string">&quot;2014-11-15&quot;</span> <span class="string">&quot;2014-05-11&quot;</span> ...</span><br></pre></td></tr></table></figure></p><h3 id="数据框中的数据摘要"><a href="#数据框中的数据摘要" class="headerlink" title="数据框中的数据摘要"></a>数据框中的数据摘要</h3><p>可以通过应用 <code>summary()</code> 函数获取数据的统计摘要和性质。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data frame.</span></span><br><span class="line">emp.data &lt;- data.frame(</span><br><span class="line">   emp_id = <span class="built_in">c</span>(<span class="number">1</span>:<span class="number">5</span>), </span><br><span class="line">   emp_name = <span class="built_in">c</span>(<span class="string">&quot;Rick&quot;</span>, <span class="string">&quot;Dan&quot;</span>, <span class="string">&quot;Michelle&quot;</span>, <span class="string">&quot;Ryan&quot;</span>, <span class="string">&quot;Gary&quot;</span>), </span><br><span class="line">   salary = <span class="built_in">c</span>(<span class="number">623.3</span>, <span class="number">515.2</span>, <span class="number">611.0</span>, <span class="number">729.0</span>, <span class="number">843.25</span>), </span><br><span class="line">   start_date = as.Date(<span class="built_in">c</span>(<span class="string">&quot;2012-01-01&quot;</span>, <span class="string">&quot;2013-09-23&quot;</span>, <span class="string">&quot;2014-11-15&quot;</span>, <span class="string">&quot;2014-05-11&quot;</span>, <span class="string">&quot;2015-03-27&quot;</span>)), </span><br><span class="line">   stringsAsFactors = <span class="literal">FALSE</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Print the summary.</span></span><br><span class="line">print(summary(emp.data))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">     emp_id    emp_name             salary        start_date        </span><br><span class="line"> Min.   :<span class="number">1</span>   Length:<span class="number">5</span>           Min.   :<span class="number">515.2</span>   Min.   :<span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span>  </span><br><span class="line"> <span class="number">1</span>st Qu.:<span class="number">2</span>   Class :character   <span class="number">1</span>st Qu.:<span class="number">611.0</span>   <span class="number">1</span>st Qu.:<span class="number">2013</span>-<span class="number">09</span>-<span class="number">23</span>  </span><br><span class="line"> Median :<span class="number">3</span>   Mode  :character   Median :<span class="number">623.3</span>   Median :<span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>  </span><br><span class="line"> Mean   :<span class="number">3</span>                      Mean   :<span class="number">664.4</span>   Mean   :<span class="number">2014</span>-<span class="number">01</span>-<span class="number">14</span>  </span><br><span class="line"> <span class="number">3</span>rd Qu.:<span class="number">4</span>                      <span class="number">3</span>rd Qu.:<span class="number">729.0</span>   <span class="number">3</span>rd Qu.:<span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>  </span><br><span class="line"> Max.   :<span class="number">5</span>                      Max.   :<span class="number">843.2</span>   Max.   :<span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span></span><br></pre></td></tr></table></figure></p><h3 id="从数据框提取数据"><a href="#从数据框提取数据" class="headerlink" title="从数据框提取数据"></a>从数据框提取数据</h3><p>使用列名称从数据框中提取特定列。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data frame.</span></span><br><span class="line">emp.data &lt;- data.frame(</span><br><span class="line">   emp_id = <span class="built_in">c</span>(<span class="number">1</span>:<span class="number">5</span>), </span><br><span class="line">   emp_name = <span class="built_in">c</span>(<span class="string">&quot;Rick&quot;</span>, <span class="string">&quot;Dan&quot;</span>, <span class="string">&quot;Michelle&quot;</span>, <span class="string">&quot;Ryan&quot;</span>, <span class="string">&quot;Gary&quot;</span>), </span><br><span class="line">   salary = <span class="built_in">c</span>(<span class="number">623.3</span>, <span class="number">515.2</span>, <span class="number">611.0</span>, <span class="number">729.0</span>, <span class="number">843.25</span>), </span><br><span class="line">   start_date = as.Date(<span class="built_in">c</span>(<span class="string">&quot;2012-01-01&quot;</span>, <span class="string">&quot;2013-09-23&quot;</span>, <span class="string">&quot;2014-11-15&quot;</span>, <span class="string">&quot;2014-05-11&quot;</span>, <span class="string">&quot;2015-03-27&quot;</span>)), </span><br><span class="line">   stringsAsFactors = <span class="literal">FALSE</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Extract Specific columns.</span></span><br><span class="line">result &lt;- data.frame(emp.data$emp_name, emp.data$salary)</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  emp.data.emp_name emp.data.salary</span><br><span class="line">1              Rick          <span class="number">623.30</span></span><br><span class="line">2               Dan          <span class="number">515.20</span></span><br><span class="line">3          Michelle          <span class="number">611.00</span></span><br><span class="line">4              Ryan          <span class="number">729.00</span></span><br><span class="line">5              Gary          <span class="number">843.25</span></span><br></pre></td></tr></table></figure></p><p>先提取前两行，然后提取所有列</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data frame.</span></span><br><span class="line">emp.data &lt;- data.frame(</span><br><span class="line">   emp_id = <span class="built_in">c</span>(<span class="number">1</span>:<span class="number">5</span>), </span><br><span class="line">   emp_name = <span class="built_in">c</span>(<span class="string">&quot;Rick&quot;</span>, <span class="string">&quot;Dan&quot;</span>, <span class="string">&quot;Michelle&quot;</span>, <span class="string">&quot;Ryan&quot;</span>, <span class="string">&quot;Gary&quot;</span>), </span><br><span class="line">   salary = <span class="built_in">c</span>(<span class="number">623.3</span>, <span class="number">515.2</span>, <span class="number">611.0</span>, <span class="number">729.0</span>, <span class="number">843.25</span>), </span><br><span class="line">   start_date = as.Date(<span class="built_in">c</span>(<span class="string">&quot;2012-01-01&quot;</span>, <span class="string">&quot;2013-09-23&quot;</span>, <span class="string">&quot;2014-11-15&quot;</span>, <span class="string">&quot;2014-05-11&quot;</span>, <span class="string">&quot;2015-03-27&quot;</span>)), </span><br><span class="line">   stringsAsFactors = <span class="literal">FALSE</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Extract first two rows.</span></span><br><span class="line">result &lt;- emp.data[<span class="number">1</span>:<span class="number">2</span>, ]</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  emp_id    emp_name   salary    start_date</span><br><span class="line">1      <span class="number">1</span>     Rick      <span class="number">623.3</span>     <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span></span><br><span class="line">2      <span class="number">2</span>     Dan       <span class="number">515.2</span>     <span class="number">2013</span>-<span class="number">09</span>-<span class="number">23</span></span><br></pre></td></tr></table></figure></p><p>提取第 2 列和第 4 列，第 3 行和第 5 行</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data frame.</span></span><br><span class="line">emp.data &lt;- data.frame(</span><br><span class="line">   emp_id = <span class="built_in">c</span>(<span class="number">1</span>:<span class="number">5</span>), </span><br><span class="line">   emp_name = <span class="built_in">c</span>(<span class="string">&quot;Rick&quot;</span>, <span class="string">&quot;Dan&quot;</span>, <span class="string">&quot;Michelle&quot;</span>, <span class="string">&quot;Ryan&quot;</span>, <span class="string">&quot;Gary&quot;</span>), </span><br><span class="line">   salary = <span class="built_in">c</span>(<span class="number">623.3</span>, <span class="number">515.2</span>, <span class="number">611.0</span>, <span class="number">729.0</span>, <span class="number">843.25</span>), </span><br><span class="line">   start_date = as.Date(<span class="built_in">c</span>(<span class="string">&quot;2012-01-01&quot;</span>, <span class="string">&quot;2013-09-23&quot;</span>, <span class="string">&quot;2014-11-15&quot;</span>, <span class="string">&quot;2014-05-11&quot;</span>, <span class="string">&quot;2015-03-27&quot;</span>)), </span><br><span class="line">   stringsAsFactors = <span class="literal">FALSE</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Extract 3rd and 5th row with 2nd and 4th column.</span></span><br><span class="line">result &lt;- emp.data[<span class="built_in">c</span>(<span class="number">3</span>, <span class="number">5</span>), <span class="built_in">c</span>(<span class="number">2</span>, <span class="number">4</span>)]</span><br><span class="line">print(result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  emp_name start_date</span><br><span class="line">3 Michelle <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span></span><br><span class="line">5     Gary <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span></span><br></pre></td></tr></table></figure></p><h3 id="扩展数据框"><a href="#扩展数据框" class="headerlink" title="扩展数据框"></a>扩展数据框</h3><p>可以通过添加列和行来扩展数据框。</p><h4 id="添加列"><a href="#添加列" class="headerlink" title="添加列"></a>添加列</h4><p>只需使用新的列名称添加列向量。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the data frame.</span></span><br><span class="line">emp.data &lt;- data.frame(</span><br><span class="line">   emp_id = <span class="built_in">c</span>(<span class="number">1</span>:<span class="number">5</span>), </span><br><span class="line">   emp_name = <span class="built_in">c</span>(<span class="string">&quot;Rick&quot;</span>, <span class="string">&quot;Dan&quot;</span>, <span class="string">&quot;Michelle&quot;</span>, <span class="string">&quot;Ryan&quot;</span>, <span class="string">&quot;Gary&quot;</span>), </span><br><span class="line">   salary = <span class="built_in">c</span>(<span class="number">623.3</span>, <span class="number">515.2</span>, <span class="number">611.0</span>, <span class="number">729.0</span>, <span class="number">843.25</span>), </span><br><span class="line">   start_date = as.Date(<span class="built_in">c</span>(<span class="string">&quot;2012-01-01&quot;</span>, <span class="string">&quot;2013-09-23&quot;</span>, <span class="string">&quot;2014-11-15&quot;</span>, <span class="string">&quot;2014-05-11&quot;</span>, <span class="string">&quot;2015-03-27&quot;</span>)), </span><br><span class="line">   stringsAsFactors = <span class="literal">FALSE</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Add the &quot;dept&quot; coulmn.</span></span><br><span class="line">emp.data$dept &lt;- <span class="built_in">c</span>(<span class="string">&quot;IT&quot;</span>, <span class="string">&quot;Operations&quot;</span>, <span class="string">&quot;IT&quot;</span>, <span class="string">&quot;HR&quot;</span>, <span class="string">&quot;Finance&quot;</span>)</span><br><span class="line">v &lt;- emp.data</span><br><span class="line">print(v)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  emp_id   emp_name    salary    start_date       dept</span><br><span class="line">1     <span class="number">1</span>    Rick        <span class="number">623.30</span>    <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span>       IT</span><br><span class="line">2     <span class="number">2</span>    Dan         <span class="number">515.20</span>    <span class="number">2013</span>-<span class="number">09</span>-<span class="number">23</span>       Operations</span><br><span class="line">3     <span class="number">3</span>    Michelle    <span class="number">611.00</span>    <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>       IT</span><br><span class="line">4     <span class="number">4</span>    Ryan        <span class="number">729.00</span>    <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>       HR</span><br><span class="line">5     <span class="number">5</span>    Gary        <span class="number">843.25</span>    <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span>       Finance</span><br></pre></td></tr></table></figure></p><h4 id="添加行"><a href="#添加行" class="headerlink" title="添加行"></a>添加行</h4><p>要将更多行永久添加到现有数据框，我们需要引入与现有数据框相同结构的新行，并使用 <code>rbind()</code> 函数。</p><p>在下面的示例中，我们创建一个包含新行的数据框，并将其与现有数据框合并以创建最终数据框。</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create the first data frame.</span></span><br><span class="line">emp.data &lt;- data.frame(</span><br><span class="line">   emp_id = <span class="built_in">c</span>(<span class="number">1</span>:<span class="number">5</span>), </span><br><span class="line">   emp_name = <span class="built_in">c</span>(<span class="string">&quot;Rick&quot;</span>, <span class="string">&quot;Dan&quot;</span>, <span class="string">&quot;Michelle&quot;</span>, <span class="string">&quot;Ryan&quot;</span>, <span class="string">&quot;Gary&quot;</span>), </span><br><span class="line">   salary = <span class="built_in">c</span>(<span class="number">623.3</span>, <span class="number">515.2</span>, <span class="number">611.0</span>, <span class="number">729.0</span>, <span class="number">843.25</span>), </span><br><span class="line">   start_date = as.Date(<span class="built_in">c</span>(<span class="string">&quot;2012-01-01&quot;</span>, <span class="string">&quot;2013-09-23&quot;</span>, <span class="string">&quot;2014-11-15&quot;</span>, <span class="string">&quot;2014-05-11&quot;</span>, <span class="string">&quot;2015-03-27&quot;</span>)), </span><br><span class="line">   dept = <span class="built_in">c</span>(<span class="string">&quot;IT&quot;</span>, <span class="string">&quot;Operations&quot;</span>, <span class="string">&quot;IT&quot;</span>, <span class="string">&quot;HR&quot;</span>, <span class="string">&quot;Finance&quot;</span>),</span><br><span class="line">   stringsAsFactors = <span class="literal">FALSE</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Create the second data frame</span></span><br><span class="line">emp.newdata &lt;- data.frame(</span><br><span class="line">   emp_id = <span class="built_in">c</span>(<span class="number">6</span>:<span class="number">8</span>), </span><br><span class="line">   emp_name = <span class="built_in">c</span>(<span class="string">&quot;Rasmi&quot;</span>, <span class="string">&quot;Pranab&quot;</span>, <span class="string">&quot;Tusar&quot;</span>),</span><br><span class="line">   salary = <span class="built_in">c</span>(<span class="number">578.0</span>, <span class="number">722.5</span>, <span class="number">632.8</span>), </span><br><span class="line">   start_date = as.Date(<span class="built_in">c</span>(<span class="string">&quot;2013-05-21&quot;</span>, <span class="string">&quot;2013-07-30&quot;</span>, <span class="string">&quot;2014-06-17&quot;</span>)),</span><br><span class="line">   dept = <span class="built_in">c</span>(<span class="string">&quot;IT&quot;</span>, <span class="string">&quot;Operations&quot;</span>, <span class="string">&quot;Fianance&quot;</span>),</span><br><span class="line">   stringsAsFactors = <span class="literal">FALSE</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Bind the two data frames.</span></span><br><span class="line">emp.finaldata &lt;- rbind(emp.data, emp.newdata)</span><br><span class="line">print(emp.finaldata)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当我们执行上面的代码，它产生以下结果：</span></span><br><span class="line"></span><br><span class="line">  emp_id     emp_name    salary     start_date       dept</span><br><span class="line">1      <span class="number">1</span>     Rick        <span class="number">623.30</span>     <span class="number">2012</span>-<span class="number">01</span>-<span class="number">01</span>       IT</span><br><span class="line">2      <span class="number">2</span>     Dan         <span class="number">515.20</span>     <span class="number">2013</span>-<span class="number">09</span>-<span class="number">23</span>       Operations</span><br><span class="line">3      <span class="number">3</span>     Michelle    <span class="number">611.00</span>     <span class="number">2014</span>-<span class="number">11</span>-<span class="number">15</span>       IT</span><br><span class="line">4      <span class="number">4</span>     Ryan        <span class="number">729.00</span>     <span class="number">2014</span>-<span class="number">05</span>-<span class="number">11</span>       HR</span><br><span class="line">5      <span class="number">5</span>     Gary        <span class="number">843.25</span>     <span class="number">2015</span>-<span class="number">03</span>-<span class="number">27</span>       Finance</span><br><span class="line">6      <span class="number">6</span>     Rasmi       <span class="number">578.00</span>     <span class="number">2013</span>-<span class="number">05</span>-<span class="number">21</span>       IT</span><br><span class="line">7      <span class="number">7</span>     Pranab      <span class="number">722.50</span>     <span class="number">2013</span>-<span class="number">07</span>-<span class="number">30</span>       Operations</span><br><span class="line">8      <span class="number">8</span>     Tusar       <span class="number">632.80</span>     <span class="number">2014</span>-<span class="number">06</span>-<span class="number">17</span>       Fianance</span><br></pre></td></tr></table></figure></p><h2 id="自带数据集"><a href="#自带数据集" class="headerlink" title="自带数据集"></a>自带数据集</h2><p>使用以下命令可以查询 R 语言自带的数据集：</p><p><figure class="highlight r"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">data()</span><br></pre></td></tr></table></figure></p><h3 id="向量数据集"><a href="#向量数据集" class="headerlink" title="向量数据集"></a>向量数据集</h3><div class="table-container"><table><thead><tr><th style="text-align:center">数据集</th><th style="text-align:center">内容</th></tr></thead><tbody><tr><td style="text-align:center">euro</td><td style="text-align:center">欧元汇率，长度为 11，每个元素都有命名</td></tr><tr><td style="text-align:center">landmasses</td><td style="text-align:center">48 个陆地的面积，每个都有命名</td></tr><tr><td style="text-align:center">precip</td><td style="text-align:center">长度为 70 的命名向量</td></tr><tr><td style="text-align:center">rivers</td><td style="text-align:center">北美 141 条河流长度</td></tr><tr><td style="text-align:center">state.abb</td><td style="text-align:center">美国 50 个州的双字母缩写</td></tr><tr><td style="text-align:center">state.area</td><td style="text-align:center">美国 50 个州的面积</td></tr><tr><td style="text-align:center">state.name</td><td style="text-align:center">美国 50 个州的全称</td></tr></tbody></table></div><h3 id="因子数据集"><a href="#因子数据集" class="headerlink" title="因子数据集"></a>因子数据集</h3><div class="table-container"><table><thead><tr><th style="text-align:center">数据集</th><th style="text-align:center">内容</th></tr></thead><tbody><tr><td style="text-align:center">state.division</td><td style="text-align:center">美国 50 个州的分类，9 个类别</td></tr><tr><td style="text-align:center">state.region</td><td style="text-align:center">美国 50 个州的地理分类</td></tr></tbody></table></div><h3 id="矩阵、数组数据集"><a href="#矩阵、数组数据集" class="headerlink" title="矩阵、数组数据集"></a>矩阵、数组数据集</h3><div class="table-container"><table><thead><tr><th style="text-align:center">数据集</th><th style="text-align:center">内容</th></tr></thead><tbody><tr><td style="text-align:center">euro.cross</td><td style="text-align:center">11 种货币的汇率矩阵</td></tr><tr><td style="text-align:center">freeny</td><td style="text-align:center">每个季度影响收入四个因素的记录</td></tr><tr><td style="text-align:center">state.x77</td><td style="text-align:center">美国 50 个州的八个指标</td></tr><tr><td style="text-align:center">USPersonalExpenditure</td><td style="text-align:center">5 个年份在 5 个消费方向的数据</td></tr><tr><td style="text-align:center">VADeaths</td><td style="text-align:center">1940 年弗吉尼亚州死亡率（每千人）</td></tr><tr><td style="text-align:center">volcano</td><td style="text-align:center">某火山区的地理信息（10 米 × 10 米的网格）</td></tr><tr><td style="text-align:center">WorldPhones</td><td style="text-align:center">8 个区域在 7 个年份的电话总数</td></tr><tr><td style="text-align:center">iris3</td><td style="text-align:center">3 种鸢尾花形态数据</td></tr><tr><td style="text-align:center">Titanic</td><td style="text-align:center">泰坦尼克乘员统计</td></tr><tr><td style="text-align:center">UCBAdmissions</td><td style="text-align:center">伯克利分校 1973 年院系、录取和性别的频数</td></tr><tr><td style="text-align:center">crimtab</td><td style="text-align:center">3000 个男性罪犯左手中指长度和身高关系</td></tr><tr><td style="text-align:center">HairEyeColor</td><td style="text-align:center">592 人头发颜色、眼睛颜色和性别的频数</td></tr><tr><td style="text-align:center">occupationalStatus</td><td style="text-align:center">英国男性父子职业联系</td></tr></tbody></table></div><h3 id="类矩阵数据集"><a href="#类矩阵数据集" class="headerlink" title="类矩阵数据集"></a>类矩阵数据集</h3><div class="table-container"><table><thead><tr><th style="text-align:center">数据集</th><th style="text-align:center">内容</th></tr></thead><tbody><tr><td style="text-align:center">eurodist</td><td style="text-align:center">欧洲 12 个城市的距离矩阵，只有下三角部分</td></tr><tr><td style="text-align:center">Harman23.cor</td><td style="text-align:center">305 个女孩 8 个形态指标的相关系数矩阵</td></tr><tr><td style="text-align:center">Harman74.cor</td><td style="text-align:center">145 个儿童 24 个心理指标的相关系数矩阵</td></tr></tbody></table></div><h3 id="数据框数据集"><a href="#数据框数据集" class="headerlink" title="数据框数据集"></a>数据框数据集</h3><div class="table-container"><table><thead><tr><th style="text-align:center">数据集</th><th style="text-align:center">内容</th></tr></thead><tbody><tr><td style="text-align:center">airquality</td><td style="text-align:center">纽约 1973 年 5-9 月每日空气质量</td></tr><tr><td style="text-align:center">anscombe</td><td style="text-align:center">四组 x-y 数据，虽有相似的统计量，但实际数据差别较大</td></tr><tr><td style="text-align:center">attenu</td><td style="text-align:center">多个观测站对加利福尼亚 23 次地震的观测数据</td></tr><tr><td style="text-align:center">attitude</td><td style="text-align:center">30 个部门在七个方面的调查结果，调查结果是同一部门 35 个职员赞成的百分比</td></tr><tr><td style="text-align:center">beaver1</td><td style="text-align:center">一只海狸每 10 分钟的体温数据，共 114 条数据</td></tr><tr><td style="text-align:center">beaver2</td><td style="text-align:center">另一只海狸每 10 分钟的体温数据，共 100 条数据</td></tr><tr><td style="text-align:center">BOD</td><td style="text-align:center">随水质的提高，生化反应对氧的需求（mg/l）随时间（天）的变化</td></tr><tr><td style="text-align:center">cars</td><td style="text-align:center">1920 年代汽车速度对刹车距离的影响</td></tr><tr><td style="text-align:center">chickwts</td><td style="text-align:center">不同饮食种类对小鸡生长速度的影响</td></tr><tr><td style="text-align:center">esoph</td><td style="text-align:center">法国的一个食管癌病例对照研究</td></tr><tr><td style="text-align:center">faithful</td><td style="text-align:center">一个间歇泉的爆发时间和持续时间</td></tr><tr><td style="text-align:center">Formaldehyde</td><td style="text-align:center">两种方法测定甲醛浓度时分光光度计的读数</td></tr><tr><td style="text-align:center">Freeny</td><td style="text-align:center">每季度收入和其他四因素的记录</td></tr><tr><td style="text-align:center">dating from</td><td style="text-align:center">配对的病例对照数据，用于条件 logistic 回归</td></tr><tr><td style="text-align:center">InsectSprays</td><td style="text-align:center">使用不同杀虫剂时昆虫数目</td></tr><tr><td style="text-align:center">iris</td><td style="text-align:center">3 种鸢尾花形态数据</td></tr><tr><td style="text-align:center">LifeCycleSavings</td><td style="text-align:center">50 个国家的存款率</td></tr><tr><td style="text-align:center">longley</td><td style="text-align:center">强共线性的宏观经济数据</td></tr><tr><td style="text-align:center">morley</td><td style="text-align:center">光速测量试验数据</td></tr><tr><td style="text-align:center">mtcars</td><td style="text-align:center">32 辆汽车在 11 个指标上的数据</td></tr><tr><td style="text-align:center">OrchardSprays</td><td style="text-align:center">使用拉丁方设计研究不同喷雾剂对蜜蜂的影响</td></tr><tr><td style="text-align:center">PlantGrowth</td><td style="text-align:center">三种处理方式对植物产量的影响</td></tr><tr><td style="text-align:center">pressure</td><td style="text-align:center">温度和气压</td></tr><tr><td style="text-align:center">Puromycin</td><td style="text-align:center">两种细胞中辅因子浓度对酶促反应的影响</td></tr><tr><td style="text-align:center">quakes</td><td style="text-align:center">1000 次地震观测数据（震级 4）</td></tr><tr><td style="text-align:center">randu</td><td style="text-align:center">使用 FORTRAN 中的 RANDU 三个一组生成随机数字，共 400 组。</td></tr><tr><td style="text-align:center">rock</td><td style="text-align:center">48 块石头的形态数据</td></tr><tr><td style="text-align:center">sleep</td><td style="text-align:center">两药物的催眠效果</td></tr><tr><td style="text-align:center">stackloss</td><td style="text-align:center">化工厂将氨转为硝酸的数据</td></tr><tr><td style="text-align:center">swiss</td><td style="text-align:center">瑞士生育率和社会经济指标</td></tr><tr><td style="text-align:center">ToothGrowth</td><td style="text-align:center">VC 剂量和摄入方式对豚鼠牙齿的影响</td></tr><tr><td style="text-align:center">trees</td><td style="text-align:center">树木形态指标</td></tr><tr><td style="text-align:center">USArrests</td><td style="text-align:center">美国 50 个州的四个犯罪率指标</td></tr><tr><td style="text-align:center">USJudgeRatings</td><td style="text-align:center">43 名律师的 12 个评价指标</td></tr><tr><td style="text-align:center">warpbreaks</td><td style="text-align:center">织布机异常数据</td></tr><tr><td style="text-align:center">women</td><td style="text-align:center">15 名女性的身高和体重</td></tr></tbody></table></div><h3 id="列表数据集"><a href="#列表数据集" class="headerlink" title="列表数据集"></a>列表数据集</h3><div class="table-container"><table><thead><tr><th style="text-align:center">数据集</th><th style="text-align:center">内容</th></tr></thead><tbody><tr><td style="text-align:center">state.center</td><td style="text-align:center">美国 50 个州中心的经度和纬度</td></tr></tbody></table></div><h3 id="类数据框数据集"><a href="#类数据框数据集" class="headerlink" title="类数据框数据集"></a>类数据框数据集</h3><div class="table-container"><table><thead><tr><th style="text-align:center">数据集</th><th style="text-align:center">内容</th></tr></thead><tbody><tr><td style="text-align:center">ChickWeight</td><td style="text-align:center">饮食对鸡生长的影响</td></tr><tr><td style="text-align:center">CO2</td><td style="text-align:center">耐寒植物 CO2 摄取的差异</td></tr><tr><td style="text-align:center">DNase</td><td style="text-align:center">若干次试验中，DNase 浓度和光密度的关系</td></tr><tr><td style="text-align:center">Indometh</td><td style="text-align:center">某药物的药物动力学数据</td></tr><tr><td style="text-align:center">Loblolly</td><td style="text-align:center">火炬松的高度、年龄和种源</td></tr><tr><td style="text-align:center">Orange</td><td style="text-align:center">桔子树生长数据</td></tr><tr><td style="text-align:center">Theoph</td><td style="text-align:center">茶碱药动学数据</td></tr></tbody></table></div><h3 id="时间序列数据集"><a href="#时间序列数据集" class="headerlink" title="时间序列数据集"></a>时间序列数据集</h3><div class="table-container"><table><thead><tr><th style="text-align:center">数据集</th><th style="text-align:center">内容</th></tr></thead><tbody><tr><td style="text-align:center">airmiles</td><td style="text-align:center">美国 1937-1960 年客运里程营收（实际售出机位乘以飞行哩数）</td></tr><tr><td style="text-align:center">AirPassengers</td><td style="text-align:center">Box &amp; Jenkins 航空公司 1949-1960 年每月国际航线乘客数</td></tr><tr><td style="text-align:center">austres</td><td style="text-align:center">澳大利亚 1971-1994 每季度人口数（以千为单位）</td></tr><tr><td style="text-align:center">BJsales</td><td style="text-align:center">有关销售的一个时间序列</td></tr><tr><td style="text-align:center">BJsales.lead</td><td style="text-align:center">前一指标的先行指标（leading indicator）</td></tr><tr><td style="text-align:center">co2</td><td style="text-align:center">1959-1997 年每月大气 co2 浓度（ppm）</td></tr><tr><td style="text-align:center">discoveries</td><td style="text-align:center">1860-1959 年每年巨大发现或发明的个数</td></tr><tr><td style="text-align:center">ldeaths</td><td style="text-align:center">1974-1979 年英国每月支气管炎、肺气肿和哮喘的死亡率</td></tr><tr><td style="text-align:center">fdeaths</td><td style="text-align:center">前述死亡率的女性部分</td></tr><tr><td style="text-align:center">mdeaths</td><td style="text-align:center">前述死亡率的男性部分</td></tr><tr><td style="text-align:center">freeny.y</td><td style="text-align:center">每季度收入</td></tr><tr><td style="text-align:center">JohnsonJohnson</td><td style="text-align:center">1960-1980 年每季度 Johnson &amp; Johnson 股票的红利</td></tr><tr><td style="text-align:center">LakeHuron</td><td style="text-align:center">1875-1972 年某一湖泊水位的记录</td></tr><tr><td style="text-align:center">lh</td><td style="text-align:center">黄体生成素水平，10 分钟测量一次</td></tr><tr><td style="text-align:center">lynx</td><td style="text-align:center">1821-1934 年加拿大猞猁数据</td></tr><tr><td style="text-align:center">nhtemp</td><td style="text-align:center">1912-1971 年每年平均温度</td></tr><tr><td style="text-align:center">Nile</td><td style="text-align:center">1871-1970 尼罗河流量</td></tr><tr><td style="text-align:center">nottem</td><td style="text-align:center">1920-1939 每月大气温度</td></tr><tr><td style="text-align:center">presidents</td><td style="text-align:center">1945-1974 年每季度美国总统支持率</td></tr><tr><td style="text-align:center">UKDriverDeaths</td><td style="text-align:center">1969-1984 年每月英国司机死亡或严重伤害的数目</td></tr><tr><td style="text-align:center">sunspot.month</td><td style="text-align:center">1749-1997 每月太阳黑子数</td></tr><tr><td style="text-align:center">sunspot.year</td><td style="text-align:center">1700-1988 每年太阳黑子数</td></tr><tr><td style="text-align:center">sunspots</td><td style="text-align:center">1749-1983 每月太阳黑子数</td></tr><tr><td style="text-align:center">treering</td><td style="text-align:center">归一化的树木年轮数据</td></tr><tr><td style="text-align:center">UKgas</td><td style="text-align:center">1960-1986 每月英国天然气消耗</td></tr><tr><td style="text-align:center">USAccDeaths</td><td style="text-align:center">1973-1978 美国每月意外死亡人数</td></tr><tr><td style="text-align:center">uspop</td><td style="text-align:center">1790–1970 美国每十年一次的人口总数（百万为单位）</td></tr><tr><td style="text-align:center">WWWusage</td><td style="text-align:center">每分钟网络连接数</td></tr><tr><td style="text-align:center">Seatbelts</td><td style="text-align:center">多变量时间序列。和 UKDriverDeaths 时间段相同，反映更多因素。</td></tr><tr><td style="text-align:center">EuStockMarkets</td><td style="text-align:center">多变量时间序列。欧洲股市四个主要指标的每个工作日记录，共 1860 条记录。</td></tr></tbody></table></div>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/R/">R</category>
      
      
      <comments>https://blog.eurkon.com/post/de0e67e.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>MySQL 优化</title>
      <link>https://blog.eurkon.com/post/d7961cf0.html</link>
      <guid>https://blog.eurkon.com/post/d7961cf0.html</guid>
      <pubDate>Thu, 14 Jan 2021 05:32:11 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;优化三大方向&quot;&gt;&lt;a href=&quot;#优化三大方向&quot; class=&quot;headerlink&quot; title=&quot;优化三大方向&quot;&gt;&lt;/a&gt;优化三大方向&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;优化 MySQL 所在服务器内核（此优化一般由运维人员完成）。&lt;/li&gt;
&lt;li&gt;对 MySQL</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="优化三大方向"><a href="#优化三大方向" class="headerlink" title="优化三大方向"></a>优化三大方向</h2><ul><li>优化 MySQL 所在服务器内核（此优化一般由运维人员完成）。</li><li>对 MySQL 配置参数进行优化（my.cnf）此优化需要进行压力测试来进行参数调整。</li><li>对 SQL 语句以及表优化。</li></ul><h2 id="参数优化"><a href="#参数优化" class="headerlink" title="参数优化"></a>参数优化</h2><ol><li><p>MySQL 默认的最大连接数为 100，可以在 <code>mysql</code> 命令提示窗口使用以下命令查看</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SHOW variables LIKE &#x27;max_connections&#x27;;</span><br></pre></td></tr></table></figure></p></li><li><p>查看当前访问 MySQL 的线程</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SHOW processlist;</span><br></pre></td></tr></table></figure></p></li><li><p>设置最大连接数</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SET globle max_connections = 5000;</span><br></pre></td></tr></table></figure></p><p> 最大可设置 <code>16384</code>，超过没用</p></li><li><p>查看当前被使用的 connections</p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SHOW GLOBAL STATUS LIKE &#x27;max_user_connections&#x27;</span><br></pre></td></tr></table></figure></p></li></ol><h2 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h2><ul><li><a href="#使用查询缓存优化查询">使用查询缓存优化查询</a></li><li><a href="#使用-EXPLAIN-关键字检测查询">使用 EXPLAIN 关键字检测查询</a></li><li><a href="#只要一行数据时使用-LIMIT-1">只要一行数据时使用 LIMIT 1</a></li><li><a href="#为搜索字段建立索引">为搜索字段建立索引</a></li><li><a href="#在-JOIN-表的时候使用相当类型的列并将其索引">在 JOIN 表的时候使用相当类型的列并将其索引</a></li><li><a href="#切记不要使用-ORDER-BY-RAND">切记不要使用 ORDER BY RAND()</a></li><li><a href="#避免使用-SELECT">避免使用 SELECT *</a></li><li><a href="#永远为每张表设置一个-ID-主键">永远为每张表设置一个 ID 主键</a></li><li><a href="#可以使用-ENUM-则不用-VARCHAR">可以使用 ENUM 则不用 VARCHAR</a></li><li><a href="#尽可能的不要赋值为-NULL">尽可能的不要赋值为 NULL</a></li><li><a href="#固定长度的表会更快">固定长度的表会更快</a></li><li><a href="#垂直分割">垂直分割</a></li><li><a href="#拆分大的-DELETE-或-INSERT-语句">拆分大的 DELETE 或 INSERT 语句</a></li><li><a href="#越小的列会越快">越小的列会越快</a></li><li><a href="#选择正确的存储引擎">选择正确的存储引擎</a></li><li><a href="#小心永久链接">小心永久链接</a></li></ul><h3 id="使用查询缓存优化查询"><a href="#使用查询缓存优化查询" class="headerlink" title="使用查询缓存优化查询"></a>使用查询缓存优化查询</h3><p>大多数的 MySQL 服务器都开启了查询缓存。这是提高性能最有效的方法之一，而且这是被 MySQL 引擎处理的。当有很多相同的查询被执行了多次的时候，这些查询结果会被放入一个缓存中，这样后续的相同查询就不用操作而直接访问缓存结果了。</p><p>这里最主要的问题是，对于我们程序员来说，这个事情是很容易被忽略的。因为我们某些查询语句会让 MySQL 不使用缓存，示例如下：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> username <span class="keyword">FROM</span> <span class="keyword">user</span> <span class="keyword">WHERE</span> signup_date &gt;= <span class="keyword">CURDATE</span>()</span><br><span class="line"><span class="keyword">SELECT</span> username <span class="keyword">FROM</span> <span class="keyword">user</span> <span class="keyword">WHERE</span> signup_date &gt;= <span class="string">&#x27;2014-06-24&#x27;</span></span><br></pre></td></tr></table></figure></p><p>上面两条 SQL 语句的差别就是 <code>CURDATE()</code> ，MySQL 的查询缓存对这个函数不起作用。所以，像 <code>NOW()</code> 和 <code>RAND()</code> 或是其它的诸如此类的 SQL 函数都不会开启查询缓存，因为这些函数的返回是会不定的易变的。所以，你所需要的就是用一个变量来代替 MySQL 的函数，从而开启缓存。</p><h3 id="使用-EXPLAIN-关键字检测查询"><a href="#使用-EXPLAIN-关键字检测查询" class="headerlink" title="使用 EXPLAIN 关键字检测查询"></a>使用 EXPLAIN 关键字检测查询</h3><p>使用 <code>EXPLAIN</code> 关键字可以使我们知道 MySQL 是如何处理 SQL 语句的，这样可以帮助我们分析我们的查询语句或是表结构的性能瓶颈；<code>EXPLAIN</code> 的查询结果还会告诉我们索引主键是如何被利用的，数据表是如何被被搜索或排序的等等。语法格式是 <code>EXPLAIN + SELECT</code> 语句：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">EXPLAIN</span> <span class="keyword">SELECT</span> &lt;字段名&gt; <span class="keyword">FROM</span> &lt;表名&gt; <span class="keyword">WHERE</span> &lt;表达式&gt;</span><br></pre></td></tr></table></figure></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/mysql_optimization/explain_1.png" alt="EXPLAIN 关键字"></p><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="/images/post/mysql_optimization/explain_2.png" alt="EXPLAIN 关键字"></p><p>我们可以看到，前一个结果显示搜索了 <code>7883</code> 行，而后一个只是搜索了两个表的 <code>9</code> 和 <code>16</code> 行。查看 <code>rows</code> 列可以让我们找到潜在的性能问题。 </p><h3 id="只要一行数据时使用-LIMIT-1"><a href="#只要一行数据时使用-LIMIT-1" class="headerlink" title="只要一行数据时使用 LIMIT 1"></a>只要一行数据时使用 LIMIT 1</h3><p>加上 <code>LIMIT 1</code> 可以增加性能。MySQL 数据库引擎会在查找到一条数据后停止搜索，而不是继续往后查询下一条符合条件的数据记录。</p><h3 id="为搜索字段建立索引"><a href="#为搜索字段建立索引" class="headerlink" title="为搜索字段建立索引"></a>为搜索字段建立索引</h3><p>索引不一定就是给主键或者是唯一的字段，如果在表中，有某个字段经常用来做搜索，需要将其建立索引。</p><p><strong>索引的有关操作如下：</strong></p><h4 id="创建索引"><a href="#创建索引" class="headerlink" title="创建索引"></a>创建索引</h4><p>在执行 <code>CREATE TABLE</code> 语句时可以创建索引，也可以单独用 <code>CREATE INDEX</code> 或 <code>ALTER TABLE</code> 来为表增加索引。</p><ol><li><p>ALTER TABLE</p><p> ALTER TABLE 用来创建普通索引、唯一索引、主键索引和全文索引</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">ADD</span> <span class="keyword">INDEX</span> index_name (column_list);</span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">ADD</span> <span class="keyword">UNIQUE</span> (column_list);</span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">ADD</span> PRIMARY <span class="keyword">KEY</span> (column_list);</span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">ADD</span> FULLTEXT (column_list);</span><br></pre></td></tr></table></figure></p><p> 其中 <code>table_name</code> 是要增加索引名的表名，<code>column_list</code> 指出对哪些列进行索引，多列时各列之间使用半角逗号隔开。索引名 <code>index_name</code> 是可选的，如果不指定索引名称，MySQL 将根据第一个索引列自动指定索引名称，另外，<code>ALTER TABLE</code> 允许在单个语句中更改多个表，因此可以在同时创建多个索引。</p></li><li><p>CREATE INDEX</p><p> <code>CREATE INDEX</code> 可对表增加普通索引或唯一索引以及全文索引，但是不可以对表增加主键索引</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">INDEX</span> index_name <span class="keyword">ON</span> table_name (column_list);</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">UNIQUE</span> index_name <span class="keyword">ON</span> table_name (column_list);</span><br><span class="line"><span class="keyword">CREATE</span> FULLTEXT index_name <span class="keyword">ON</span> table_name (column_list);</span><br></pre></td></tr></table></figure></p><p> <code>table_name</code>、<code>index_name</code> 和 <code>column_list</code> 具有与 <code>ALTER TABLE</code> 语句中相同的含义，索引名必须指定。另外，不能用 <code>CREATE INDEX</code> 语句创建 <code>PRIMARY KEY</code> 索引。</p></li></ol><h4 id="索引类型"><a href="#索引类型" class="headerlink" title="索引类型"></a>索引类型</h4><ul><li>普通索引（INDEX）：适用于 name、email 等一般属性</li><li>唯一索引（UNIQUE）：与普通索引类似，不同的是唯一索引要求索引字段值在表中是唯一的，这一点和主键索引类似，但是不同的是，唯一索引允许有空值。唯一索引一般适用于身份证号码、用户账号等不允许有重复的属性字段上。</li><li>主键索引（PRIMARY KEY）：其实就是主键，一般在建表时就指定了，不需要额外添加。</li><li>全文检索（FULLTEXT）：只适用于 <code>VARCHAR</code> 和 <code>TEXT</code> 类型的字段。</li></ul><p><strong>注意</strong>：全文索引和普通索引是有很大区别的，如果建立的是普通索引，一般会使用 <code>LIKE</code> 进行模糊查询，只会对查询内容前一部分有效，即只对前面不使用通配符的查询有效，如果前后都有通配符，普通索引将不会起作用。对于全文索引而言在查询时有自己独特的匹配方式，例如我们在对一篇文章的标题和内容进行全文索引时：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> article <span class="keyword">ADD</span> FULLTEXT (<span class="string">&#x27;title&#x27;</span>, <span class="string">&#x27;content&#x27;</span>); </span><br><span class="line"><span class="comment"># 在进行检索时就需要使用如下的语法进行检索：</span></span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> article <span class="keyword">WHERE</span> <span class="keyword">MATCH</span>(<span class="string">&#x27;title&#x27;</span>, <span class="string">&#x27;content&#x27;</span>) AGAINST (<span class="string">&#x27;查询字符串&#x27;</span>);</span><br></pre></td></tr></table></figure></p><p><strong>在使用全文检索时的注意事项：</strong></p><p>MySQL 自带的全文索引只能用于数据库引擎为 <code>MyISAM</code> 的数据表，如果是其他数据引擎，则全文索引不会生效。此外，MySQL 自带的全文索引只能对英文进行全文检索，目前无法对中文进行全文检索。如果需要对包含中文在内的文本数据进行全文检索，我们需要采用 Sphinx/Coreseek 技术来处理中文。另外使用 MySQL 自带的全文索引时，如果查询字符串的长度过短将无法得到期望的搜索结果。MySQL 全文索引所能找到的词默认最小长度为 4 个字符。另外，如果查询的字符串包含停止词，那么该停止词将会被忽略。</p><h4 id="组合索引"><a href="#组合索引" class="headerlink" title="组合索引"></a>组合索引</h4><p>组合索引又称多列索引，就是建立索引时指定多个字段属性。有点类似于字典目录，比如查询 <code>guo</code> 这个拼音的字时，首先查找 <code>g</code> 字母，然后在 <code>g</code> 的检索范围内查询第二个字母为 <code>u</code> 的列表，最后在 <code>u</code> 的范围内查找最后一个字母为 <code>o</code> 的字。比如组合索引 <code>(a,b,c)</code>，<code>abc</code> 都是排好序的，在任意一段 <code>a</code> 的下面 <code>b</code> 都是排好序的，任何一段 <code>b</code> 下面 <code>c</code> 都是排好序的。</p><p>组合索引的生效原则是：从前往后依次使用生效，如果中间某个索引没有使用，那么断点前面的索引部分起作用，断点后面的索引没有起作用。</p><p>造成断点的原因：</p><ul><li>前边的任意一个索引没有参与查询，后边的全部不生效。</li><li>前边的任意一个索引字段参与的是范围查询，后面的不会生效。</li><li>断点跟索引字字段在 SQL 语句中的位置前后无关，只与是否存在有关。</li></ul><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">(0) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> a=<span class="number">3</span> <span class="keyword">AND</span> b=<span class="number">45</span> <span class="keyword">AND</span> c=<span class="number">5</span> ...</span><br><span class="line"><span class="comment"># 这种三个索引顺序使用中间没有断点，全部发挥作用</span></span><br><span class="line">(<span class="number">1</span>) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> a=<span class="number">3</span> <span class="keyword">AND</span> c=<span class="number">5</span> ...</span><br><span class="line"><span class="comment"># 这种情况下 b 就是断点，a 发挥了效果，c 没有效果</span></span><br><span class="line">(<span class="number">2</span>) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> b=<span class="number">3</span> <span class="keyword">AND</span> c=<span class="number">4</span> ...</span><br><span class="line"><span class="comment"># 这种情况下 a 就是断点，在 a 后面的索引都没有发挥作用，这种写法联合索引没有发挥任何效果</span></span><br><span class="line">(<span class="number">3</span>) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> b=<span class="number">45</span> <span class="keyword">AND</span> a=<span class="number">3</span> <span class="keyword">AND</span> c=<span class="number">5</span> ...</span><br><span class="line"><span class="comment"># 这个跟第一个一样，全部发挥作用，abc 只要用上了就行，跟写的顺序无关</span></span><br></pre></td></tr></table></figure></p><p><code>(a,b,c)</code> 三个列上加了联合索引（是联合索引，不是在每个列上单独加索引）而是建立了 <code>a</code>, <code>(a,b)</code>, <code>(a,b,c)</code>三个索引，另外 <code>(a,b,c)</code> 多列索引和 <code>(a,c,b)</code> 是不一样的。</p><p>具体实例可以说明：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">(0) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> a=<span class="number">3</span> <span class="keyword">AND</span> b=<span class="number">5</span> <span class="keyword">AND</span> c=<span class="number">4</span>;</span><br><span class="line"><span class="comment"># abc 三个索引都在 WHERE 条件里面用到了，而且都发挥了作用</span></span><br><span class="line">(1) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> c=<span class="number">4</span> <span class="keyword">AND</span> b=<span class="number">6</span> <span class="keyword">AND</span> a=<span class="number">3</span>;</span><br><span class="line"><span class="comment"># 这条语句为了说明 组合索引与在 SQL 中的位置先后无关，WHERE 里面的条件顺序在查询之前会被 MySQL 自动优化，效果跟上一句一样</span></span><br><span class="line">(2) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> a=<span class="number">3</span> <span class="keyword">AND</span> c=<span class="number">7</span>;</span><br><span class="line"><span class="comment"># a 用到索引，b 没有用，所以 c 是没有用到索引效果的</span></span><br><span class="line">(3) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> a=<span class="number">3</span> <span class="keyword">AND</span> b&gt;<span class="number">7</span> <span class="keyword">AND</span> c=<span class="number">3</span>;</span><br><span class="line"><span class="comment"># a 用到了，b 也用到了，c 没有用到，这个地方 b 是范围值，也算断点，只不过自身用到了索引</span></span><br><span class="line">(4) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> b=<span class="number">3</span> <span class="keyword">AND</span> c=<span class="number">4</span>;</span><br><span class="line"><span class="comment"># 因为 a 索引没有使用，所以这里 bc 都没有用上索引效果</span></span><br><span class="line">(5) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> a&gt;<span class="number">4</span> <span class="keyword">AND</span> b=<span class="number">7</span> <span class="keyword">AND</span> c=<span class="number">9</span>;</span><br><span class="line"><span class="comment"># a 用到了 b 没有使用，c 没有使用</span></span><br><span class="line">(6) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> a=<span class="number">3</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> b;</span><br><span class="line"><span class="comment"># a 用到了索引，b 在结果排序中也用到了索引的效果，前面说了，a 下面任意一段的 b 是排好序的</span></span><br><span class="line">(7) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> a=<span class="number">3</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> c;</span><br><span class="line"><span class="comment"># a 用到了索引，但是这个地方 c 没有发挥排序效果，因为中间断点了，使用 EXPLAIN 可以看到 filesort</span></span><br><span class="line">(8) <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> mytable <span class="keyword">WHERE</span> b=<span class="number">3</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> a;</span><br><span class="line"><span class="comment"># b 没有用到索引，排序中 a 也没有发挥索引效果</span></span><br></pre></td></tr></table></figure></p><p><strong>注意</strong>：在查询时，MySQL 只能使用一个索引，如果建立的是多个单列的普通索引，在查询时会根据查询的索引字段，从中选择一个限制最严格的单例索引进行查询。别的索引都不会生效。</p><h4 id="查看索引"><a href="#查看索引" class="headerlink" title="查看索引"></a>查看索引</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SHOW</span> <span class="keyword">INDEX</span> <span class="keyword">FROM</span> tblname;</span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">KEYS</span> <span class="keyword">FROM</span> tblname;</span><br></pre></td></tr></table></figure></p><h4 id="删除索引"><a href="#删除索引" class="headerlink" title="删除索引"></a>删除索引</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DORP INDEX IndexName ON tab_name；</span><br></pre></td></tr></table></figure></p><p><strong>注意：不能使用索引的情况</strong></p><p>对于普通索引而言在使用 <code>LIKE</code> 进行通配符模糊查询时，如果首尾之间都使用了通配符，索引时无效的。</p><p>假设查询内容的关键词为 <code>abc</code>：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> tab_name <span class="keyword">WHERE</span> index_column <span class="keyword">LIKE</span> <span class="string">&#x27;abc%&#x27;</span>; <span class="comment"># 索引是有效的</span></span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> tab_name <span class="keyword">WHERE</span> index_column <span class="keyword">LIKE</span> <span class="string">&#x27;%abc&#x27;</span>; <span class="comment"># 索引是无效的</span></span><br><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> tab_name <span class="keyword">WHERE</span> index_column <span class="keyword">LIKE</span> <span class="string">&#x27;%abc%&#x27;</span>; <span class="comment"># 索引是无效的</span></span><br></pre></td></tr></table></figure></p><p>当检索的字段内容比较大而且检索内容前后部分都不确定的情况下，可以改为全文索引，并使用特定的检索方式。</p><h3 id="在-JOIN-表的时候使用相当类型的列并将其索引"><a href="#在-JOIN-表的时候使用相当类型的列并将其索引" class="headerlink" title="在 JOIN 表的时候使用相当类型的列并将其索引"></a>在 JOIN 表的时候使用相当类型的列并将其索引</h3><p>如果在程序中有很多 <code>JOIN</code> 查询，应该保证两个表中 <code>JOIN</code> 的字段时被建立过索引的。这样 MySQL 内部会启动优化 <code>JOIN</code> 的 SQL 语句的机制。</p><p><strong>注意</strong>：这些被用来 <code>JOIN</code> 的字段，应该是相同类型的。例如：如果要把 <code>DECIMAL</code> 字段和一个 <code>INT</code> 字段 <code>JOIN</code> 在一起，MySQL 就无法使用它们的索引。对于那些 <code>STRING</code> 类型，还需要有相同的字符集才行（两个表的字符集有可能不一样）。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> company_name</span><br><span class="line"><span class="keyword">FROM</span> <span class="keyword">users</span></span><br><span class="line"><span class="keyword">LEFT</span> <span class="keyword">JOIN</span> companies</span><br><span class="line"><span class="keyword">ON</span> (users.state = companies.state)</span><br><span class="line"><span class="keyword">WHERE</span> users.id = <span class="string">&quot;user_id&quot;</span></span><br></pre></td></tr></table></figure></p><p>两个 <code>state</code> 字段应该是被建过索引的，而且应该是相当的类型，相同的字符集。</p><h3 id="切记不要使用-ORDER-BY-RAND"><a href="#切记不要使用-ORDER-BY-RAND" class="headerlink" title="切记不要使用 ORDER BY RAND()"></a>切记不要使用 ORDER BY RAND()</h3><p>如果你真的想把返回的数据行打乱了，你有 N 种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是：MySQL 会不得不去执行 <code>RAND()</code> 函数（很耗 CPU 时间），而且这是为了每一行记录去记行，然后再对其排序。就算是你用了 <code>Limit 1</code> 也无济于事（因为要排序）。</p><h3 id="避免使用-SELECT"><a href="#避免使用-SELECT" class="headerlink" title="避免使用 SELECT *"></a>避免使用 SELECT *</h3><p>从数据库里读出越多的数据，那么查询就会变得越慢。并且，如果我们的数据库服务器和 web 服务器是两台独立的服务器的话，这还会增加网络传输的负载。所以，我们应该养成一个需要什么就取什么的好的习惯。</p><p><code>Hibernate</code> 性能方面就会差，它不用 <code>*</code>，但它将整个表的所有字段全查出来。</p><h3 id="永远为每张表设置一个-ID-主键"><a href="#永远为每张表设置一个-ID-主键" class="headerlink" title="永远为每张表设置一个 ID 主键"></a>永远为每张表设置一个 ID 主键</h3><p>我们应该为数据库里的每张表都设置一个 <code>ID</code> 做为其主键，而且最好的是一个 <code>INT</code> 型的（推荐使用 <code>UNSIGNED</code>），并设置上自动增加的 <code>AUTO_INCREMENT</code> 标志。就算是我们 <code>users</code> 表有一个主键叫 <code>email</code> 的字段，我们也别让它成为主键。使用 <code>VARCHAR</code> 类型来当主键会使用得性能下降。</p><p>另外，在我们的程序中，我们应该使用表的 <code>ID</code> 来构造我们的数据结构。而且，在 MySQL 数据引擎下，还有一些操作需要使用主键，在这些情况下，主键的性能和设置变得非常重要，比如，集群，分区 ...。在这里，只有一个情况是例外，那就是“关联表”的“外键”，也就是说，这个表的主键，通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比如：有一个“学生表”有学生的 <code>ID</code>，有一个“课程表”有课程 <code>ID</code>，那么，“成绩表”就是“关联表”了，其关联了学生表和课程表，在成绩表中，学生 <code>ID</code> 和课程 <code>ID</code> 叫“外键”其共同组成主键。 </p><h3 id="可以使用-ENUM-则不用-VARCHAR"><a href="#可以使用-ENUM-则不用-VARCHAR" class="headerlink" title="可以使用 ENUM 则不用 VARCHAR"></a>可以使用 ENUM 则不用 VARCHAR</h3><p><code>ENUM</code> 类型是非常快和紧凑的。在实际上，其保存的是 <code>TINYINT</code>，但其外表上显示为字符串。这样一来，用这个字段来做一些选项列表变得相当的完美。如果我们有一个字段，比如“性别”，“国家”，“民族”，“状态”或“部门”，我们知道这些字段的取值是有限而且固定的，那么，我们应该使用 <code>ENUM</code> 而不是 <code>VARCHAR</code>。</p><h3 id="尽可能的不要赋值为-NULL"><a href="#尽可能的不要赋值为-NULL" class="headerlink" title="尽可能的不要赋值为 NULL"></a>尽可能的不要赋值为 NULL</h3><p>如果不是特殊情况，尽可能的不要使用 <code>NULL</code>。在 MySQL 中对于 INT 类型而言，<code>EMPTY</code> 是 <code>0</code>，而 <code>NULL</code> 是空值。而在 Oracle 中 <code>NULL</code> 和 <code>EMPTY</code> 的字符串是一样的。<code>NULL</code> 也需要占用存储空间，并且会使我们的程序判断时更加复杂。现实情况是很复杂的，依然会有些情况下，我们需要使用 <code>NULL</code> 值。</p><p>下面摘自 MySQL 的文档：</p><blockquote><p>NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.</p></blockquote><h3 id="固定长度的表会更快"><a href="#固定长度的表会更快" class="headerlink" title="固定长度的表会更快"></a>固定长度的表会更快</h3><p>如果表中的所有字段都是“固定长度”的，整个表会被认为是 <code>static</code> 或 <code>fixed-length</code>。例如，表中没有如下类型的字段：<code>VARCHAR</code>，<code>TEXT</code>，<code>BLOB</code>。只要我们包括了其中一个这些字段，那么这个表就不是“固定长度静态表”了，这样，MySQL 引擎会用另一种方法来处理。</p><p>固定长度的表会提高性能，因为 MySQL 搜寻得会更快一些，因为这些固定的长度是很容易计算下一个数据的偏移量的，所以读取的自然也会很快。而如果字段不是定长的，那么，每一次要找下一条的话，需要程序找到主键。并且，固定长度的表也更容易被缓存和重建。不过，唯一的副作用是，固定长度的字段会浪费一些空间，因为定长的字段无论我们用不用，他都是要分配那么多的空间。另外在取出值的时候要使用 <code>trim</code> 去除空格。</p><h3 id="垂直分割"><a href="#垂直分割" class="headerlink" title="垂直分割"></a>垂直分割</h3><p>“垂直分割”是一种把数据库中的表按列变成几张表的方法，这样可以降低表的复杂度和字段的数目，从而达到优化的目的。</p><h3 id="拆分大的-DELETE-或-INSERT-语句"><a href="#拆分大的-DELETE-或-INSERT-语句" class="headerlink" title="拆分大的 DELETE 或 INSERT 语句"></a>拆分大的 DELETE 或 INSERT 语句</h3><p>如果我们需要在一个在线的网站上去执行一个大的 <code>DELETE</code> 或 <code>INSERT</code> 查询，我们需要非常小心，要避免我们的操作让我们的整个网站停止响应。因为这两个操作是会锁表的，表一锁住了，别的操作都进不来了。</p><p>Apache 会有很多的子进程或线程。所以，其工作起来相当有效率，而我们的服务器也不希望有太多的子进程，线程和数据库链接，这是极大的占服务器资源的事情，尤其是内存。如果我们把我们的表锁上一段时间，比如 30 秒钟，那么对于一个有很高访问量的站点来说，这 30 秒所积累的访问进程/线程，数据库链接，打开的文件数，可能不仅仅会让我们的 web 服务 Crash，还可能会让我们的整台服务器马上挂了。所以在使用时使用 <code>LIMIT</code> 控制数量操作记录的数量。</p><h3 id="越小的列会越快"><a href="#越小的列会越快" class="headerlink" title="越小的列会越快"></a>越小的列会越快</h3><p>对于大多数的数据库引擎来说，硬盘操作可能是最重大的瓶颈。所以，把我们的数据变得紧凑会对这种情况非常有帮助，因为这减少了对硬盘的访问。参看 MySQL 的文档 Storage Requirements 查看所有的数据类型。</p><p>如果一个表只会有几列（比如说字典表，配置表），那么，我们就没有理由使用 <code>INT</code> 来做主键，使用 <code>MEDIUMINT</code>, <code>SMALLINT</code> 或是更小的 <code>TINYINT</code> 会更经济一些。如果我们不需要记录时间，使用 <code>DATE</code> 要比 <code>DATETIME</code> 好得多。</p><h3 id="选择正确的存储引擎"><a href="#选择正确的存储引擎" class="headerlink" title="选择正确的存储引擎"></a>选择正确的存储引擎</h3><p>在 MySQL 中有两个存储引擎 <code>MyISAM</code> 和 <code>InnoDB</code>,每个引擎都有利有弊。</p><p><code>MyISAM</code> 适合于一些需要大量查询的应用，但是对于大量写操作的支持不是很好。甚至一个 <code>UPDATE</code> 语句就会进行锁表操作，这时读取这张表的所有进程都无法进行操作直至写操作完成。另外 <code>MyISAM</code> 对于 <code>SELECT COUNT(*)</code> 这类的计算是超快无比的。<code>InnoDB</code> 的趋势会是一个非常复杂的存储引擎，对于一些小的应用，它会比 <code>MyISAM</code> 还慢。它支持“行锁”，于是在写操作比较多的时候，会更优秀。并且，他还支持更多的高级应用，比如：事务。</p><p><code>MyISAM</code> 是 MySQL5.5 版本以前默认的存储引擎，基于传统的 <code>ISAM</code> 类型，支持 <code>B-Tree</code>，全文检索，但是不是事务安全的，而且不支持外键。不具有原子性。支持锁表。</p><p><code>InnoDB</code> 是事务型引擎，支持 <code>ACID</code> 事务（实现 4 种事务隔离机制）、回滚、崩溃恢复能力、行锁。以及提供与 Oracle 一致的不加锁的读取方式。<code>InnoDB</code> 存储表和索引在一个表空间中，表空间可以包含多个文件。</p><p><code>MyISAM</code> 和 <code>InnoDB</code> 比较，如下表格所示：</p><div class="table-container"><table><thead><tr><th style="text-align:center">&nbsp;</th><th style="text-align:center">MyISAM</th><th style="text-align:center">InnoDB</th></tr></thead><tbody><tr><td style="text-align:center">事务</td><td style="text-align:center">不支持</td><td style="text-align:center">支持</td></tr><tr><td style="text-align:center">数据行锁定</td><td style="text-align:center">不支持，只有表锁定</td><td style="text-align:center">支持</td></tr><tr><td style="text-align:center">外键约束</td><td style="text-align:center">不支持</td><td style="text-align:center">支持</td></tr><tr><td style="text-align:center">表空间大小</td><td style="text-align:center">相对小</td><td style="text-align:center">相对大</td></tr><tr><td style="text-align:center">全文索引</td><td style="text-align:center">支持</td><td style="text-align:center">不支持</td></tr><tr><td style="text-align:center">关注点</td><td style="text-align:center">性能（SELECT）</td><td style="text-align:center">事务</td></tr></tbody></table></div><p><strong>注意：</strong></p><ul><li>对于 Linux 版本的 MySQL 配置文件在 <code>/etc/my.cnf</code> 中</li><li>在 5.5 之后默认的存储引擎是 <code>INNODB</code></li><li>可以单独进行修改也可以在创建表时设置<p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> tab_name <span class="keyword">ENGINE</span> <span class="keyword">INNODB</span>;</span><br></pre></td></tr></table></figure></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="keyword">IF</span> <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> tab_name(</span><br><span class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span>=utf8;</span><br></pre></td></tr></table></figure></p></li></ul><h3 id="小心永久链接"><a href="#小心永久链接" class="headerlink" title="小心永久链接"></a>小心永久链接</h3><p>“永久链接”的目的是用来减少重新创建 MySQL 链接的次数。当一个链接被创建了，它会永远处在连接的状态，就算是数据库操作已经结束了。而且，自从我们的 Apache 开始重用它的子进程后——也就是说，下一次的 HTTP 请求会重用 Apache 的子进程，并重用相同的 MySQL 链接。 </p><p>而且，Apache 运行在极端并行的环境中，会创建很多很多的了进程。这就是为什么这种“永久链接”的机制工作不好的原因。在我们决定要使用“永久链接”之前，我们需要好好地考虑一下我们的整个系统的架构。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/MySQL/">MySQL</category>
      
      
      <comments>https://blog.eurkon.com/post/d7961cf0.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>MySQL 常用命令行</title>
      <link>https://blog.eurkon.com/post/4bd4f98e.html</link>
      <guid>https://blog.eurkon.com/post/4bd4f98e.html</guid>
      <pubDate>Wed, 13 Jan 2021 01:24:44 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;连接-MySQL&quot;&gt;&lt;a href=&quot;#连接-MySQL&quot; class=&quot;headerlink&quot; title=&quot;连接 MySQL&quot;&gt;&lt;/a&gt;连接 MySQL&lt;/h2&gt;&lt;p&gt;&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="连接-MySQL"><a href="#连接-MySQL" class="headerlink" title="连接 MySQL"></a>连接 MySQL</h2><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql -h 主机地址 -u 用户名 -p 用户密码</span><br></pre></td></tr></table></figure></p><h3 id="连接到本机上的-MySQL"><a href="#连接到本机上的-MySQL" class="headerlink" title="连接到本机上的 MySQL"></a>连接到本机上的 MySQL</h3><p>首先打开 <code>DOS</code> 窗口，然后进入目录 <code>mysql\bin</code> ，再键入命令 <code>mysql -u root -p</code> 回车后提示你输密码。注意用户名前可以有空格也可以没有空格，但是密码前必须没有空格，否则让你重新输入密码。</p><p>如果刚安装好 MySQL，超级用户 <code>root</code> 是没有密码的。直接回车即可进入到 MySQL 中了，MySQL 的提示符是： <code>mysql&gt;</code></p><h3 id="连接到远程主机上的-MySQL"><a href="#连接到远程主机上的-MySQL" class="headerlink" title="连接到远程主机上的 MySQL"></a>连接到远程主机上的 MySQL</h3><p>假设远程主机的 IP 为：<code>110.110.110.110</code>，用户名为 <code>root</code>，密码为 <code>abcd123</code>。则键入以下命令：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql -h 110.110.110.110 -u root -p abcd123;</span><br></pre></td></tr></table></figure></p><p><strong>注</strong>：<code>u</code> 与 <code>root</code> 之间可以不用加空格，其它也一样。</p><h3 id="退出-MySQL-命令。"><a href="#退出-MySQL-命令。" class="headerlink" title="退出 MySQL 命令。"></a>退出 MySQL 命令。</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">exit</span><br></pre></td></tr></table></figure></p><h2 id="修改密码"><a href="#修改密码" class="headerlink" title="修改密码"></a>修改密码</h2><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqladmin -u 用户名 -p 旧密码 password 新密码</span><br></pre></td></tr></table></figure></p><ol><li><p>给 root 加个密码 ab12。</p><p> 首先在 <code>DOS</code> 下进入目录 <code>mysql\bin</code>。然后键入以下命令 <code>mysqladmin -u root -password ab12</code></p><p> <strong>注</strong>：因为开始时 <code>root</code> 没有密码，所以 <code>-p 旧密码</code> 一项就可以省略了。</p></li></ol><ol><li><p>再将 root 的密码改为 def345。</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqladmin -u root -p ab12 password def345</span><br></pre></td></tr></table></figure></p></li><li><p>增加新用户</p><p> <strong>注意</strong>：和上面不同，下面的因为是 MySQL 环境中的命令，所以后面都带一个分号作为命令结束符。</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span> <span class="keyword">ON</span> 数据库.* <span class="keyword">TO</span> 用户名@登录主机 <span class="keyword">identified</span> <span class="keyword">BY</span> <span class="string">&quot;密码&quot;</span></span><br></pre></td></tr></table></figure></p><ol><li>增加一个用户 <code>test1</code> 密码为 <code>abc</code>，让他可以在任何主机上登录，并对所有数据库有查询、插入、修改、删除的权限。首先用 <code>root</code> 用户连入 MySQL，然后键入以下命令：</li></ol><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span>, <span class="keyword">INSERT</span>, <span class="keyword">UPDATE</span>, <span class="keyword">DELETE</span> <span class="keyword">ON</span> *.* <span class="keyword">TO</span> test1@<span class="string">&quot;%&quot;</span> <span class="keyword">Identified</span> <span class="keyword">BY</span> <span class="string">&quot;abc&quot;</span>;</span><br></pre></td></tr></table></figure></p><p>但增加的用户是十分危险的，你想如某个人知道 <code>test1</code> 的密码，那么他就可以在 internet 上的任何一台电脑上登录你的 MySQL 数据库并对你的数据可以为所欲为了，解决办法见 2。</p><ol><li>增加一个用户 <code>test2</code> 密码为 <code>abc</code>，让他只可以在 <code>localhost</code> 上登录，并可以对数据库 <code>mydb</code> 进行查询、插入、修改、删除的操作（localhost 指本地主机，即 MySQL 数据库所在的那台主机）。</li></ol><p>这样用户即使用知道 <code>test2</code> 的密码，他也无法从 internet 上直接访问数据库，只能通过 MySQL 主机上的 web 页来访问了。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span>, <span class="keyword">INSERT</span>, <span class="keyword">UPDATE</span>, <span class="keyword">DELETE</span> <span class="keyword">ON</span> mydb.* <span class="keyword">TO</span> test2@localhost test2@localhost <span class="keyword">identified</span> <span class="keyword">BY</span> <span class="string">&quot;abc&quot;</span>;</span><br></pre></td></tr></table></figure></p><p>如果你不想 <code>test2</code> 有密码，可以再打一个命令将密码消掉。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span>, <span class="keyword">INSERT</span>, <span class="keyword">UPDATE</span>, <span class="keyword">DELETE</span> <span class="keyword">ON</span> mydb.* <span class="keyword">TO</span> test2@localhost test2@localhost <span class="keyword">identified</span> <span class="keyword">BY</span> <span class="string">&quot;&quot;</span>;</span><br></pre></td></tr></table></figure></p></li></ol><h2 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h2><h3 id="创建数据库"><a href="#创建数据库" class="headerlink" title="创建数据库"></a>创建数据库</h3><ol><li><p>建立一个数据库</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">DATABASE</span> &lt;数据库名&gt;;</span><br></pre></td></tr></table></figure></p></li><li><p>创建数据库并分配用户</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">DATABASE</span> &lt;数据库名&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span>, <span class="keyword">INSERT</span>, <span class="keyword">UPDATE</span>, <span class="keyword">DELETE</span>, <span class="keyword">CREATE</span>, <span class="keyword">DROP</span>, <span class="keyword">ALTER</span> <span class="keyword">ON</span> 数据库名.* <span class="keyword">TO</span> 数据库名@登录主机 <span class="keyword">IDENTIFIED</span> <span class="keyword">BY</span> <span class="string">&#x27;密码&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">SET</span> <span class="keyword">PASSWORD</span> <span class="keyword">FOR</span> <span class="string">&#x27;数据库名&#x27;</span>@<span class="string">&#x27;登录主机&#x27;</span> = <span class="string">&#x27;密码&#x27;</span>;</span><br></pre></td></tr></table></figure></p><p> 依次执行 3 个命令完成数据库创建。</p><p> <strong>注意</strong>：“密码”和“数据库”是用户自己需要设置的。</p></li></ol><h3 id="显示数据库"><a href="#显示数据库" class="headerlink" title="显示数据库"></a>显示数据库</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SHOW</span> <span class="keyword">DATABASES</span>;</span><br></pre></td></tr></table></figure></p><p><strong>注意</strong>：为了不再显示的时候乱码，要修改数据库默认编码。以下以 <code>GBK</code> 编码页面为例进行说明：</p><ol><li><p>修改 MySQL 的配置文件。</p><p> <code>my.ini</code> 文件里面修改 <code>default-character-set=gbk</code>。</p></li><li><p>代码运行时修改。</p></li></ol><ul><li><p>Java 代码：</p><p>  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jdbc:mysql:<span class="comment">//localhost:3306/test?useUnicode=true&amp;characterEncoding=gbk</span></span><br></pre></td></tr></table></figure></p></li><li><p>PHP 代码：</p><p>  <figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">header(<span class="string">&quot;Content-Type:text/html;charset=gb2312&quot;</span>);</span><br></pre></td></tr></table></figure></p></li><li><p>C 语言代码：</p><p>  <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">int mysql_set_character_set( MySQL * mysql, char * csname)；</span><br></pre></td></tr></table></figure></p><p>  该函数用于为当前连接设置默认的字符集。字符串 <code>csname</code> 指定了 1 个有效的字符集名称。连接校对成为字符集的默认校对。该函数的工作方式与 <code>SET NAMES</code> 语句类似，但它还能设置 <code>charset</code> 的值，从而影响了由 <code>mysql_real_escape_string()</code> 设置的字符集。</p></li></ul><h3 id="删除数据库"><a href="#删除数据库" class="headerlink" title="删除数据库"></a>删除数据库</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">DATABASE</span> &lt;数据库名&gt;;</span><br></pre></td></tr></table></figure></p><ol><li><p>删除一个已经确定存在的数据库</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; DROP DATABASE drop_database;</span><br><span class="line">  Query OK, 0 rows affected (0.00 sec)</span><br></pre></td></tr></table></figure></p></li><li><p>删除一个不确定存在的数据库</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; DROP DATABASE drop_database;</span><br><span class="line">ERROR 1008 (HY000): Can&#x27;t <span class="keyword">drop</span> <span class="keyword">database</span> <span class="string">&#x27;drop_database&#x27;</span>; database doesn&#x27;t exist</span><br><span class="line">//发生错误，不能删除 &#x27;drop_database&#x27; 数据库，该数据库不存在。</span><br><span class="line"></span><br><span class="line">mysql&gt; DROP DATABASE IF EXISTS drop_database;</span><br><span class="line">Query OK, 0 rows affected, 1 warning (0.00 sec)</span><br><span class="line">//产生一个警告说明此数据库不存在</span><br><span class="line"></span><br><span class="line">mysql&gt; CREATE DATABASE drop_database;</span><br><span class="line">Query OK, 1 row affected (0.00 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; DROP DATABASE IF EXISTS drop_database;</span><br><span class="line">Query OK, 0 rows affected (0.00 sec)</span><br><span class="line">//IF EXISTS 判断数据库是否存在，不存在也不产生错误</span><br></pre></td></tr></table></figure></p></li></ol><h3 id="连接数据库"><a href="#连接数据库" class="headerlink" title="连接数据库"></a>连接数据库</h3><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; USE &lt;数据库名&gt;;</span><br></pre></td></tr></table></figure></p><p>屏幕提示：<code>Database changed</code></p><p><code>USE</code> 语句可以通告 MySQL 把 <code>db_name</code> 数据库作为默认（当前）数据库使用，用于后续语句。</p><p>该数据库保持为默认数据库，直到语段的结尾，或者直到发布一个不同的 USE 语句：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; USE db1;</span><br><span class="line">mysql&gt; SELECT COUNT(*) FROM mytable;   # SELECT COUNT(*) FROM db1.mytable</span><br><span class="line">mysql&gt; USE db2;</span><br><span class="line">mysql&gt; SELECT COUNT(*) FROM mytable;   # SELECT COUNT(*) FROM db2.mytable</span><br></pre></td></tr></table></figure></p><p>使用 <code>USE</code> 语句为一个特定的当前的数据库做标记，不会阻碍您访问其它数据库中的表。</p><p>下面的例子可以从 <code>db1</code> 数据库访问作者表，并从 <code>db2</code> 数据库访问编辑表：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; USE db1;</span><br><span class="line">mysql&gt; SELECT author_name, editor_name FROM author, db2.editor WHERE author.editor_id = db2.editor.editor_id;</span><br></pre></td></tr></table></figure></p><h3 id="当前选择的数据库"><a href="#当前选择的数据库" class="headerlink" title="当前选择的数据库"></a>当前选择的数据库</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT DATABASE();</span><br><span class="line">+<span class="comment">------------+</span></span><br><span class="line">| DATABASE() |</span><br><span class="line">+<span class="comment">------------+</span></span><br><span class="line">| db1        |</span><br><span class="line">+<span class="comment">------------+</span></span><br><span class="line">1 row in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p>MySQL 中 <code>SELECT</code> 命令类似于其他编程语言里的 <code>print</code> 或者 <code>write</code>，你可以用它来显示一个字符串、数字、数学表达式的结果等等。</p><ol><li><p>显示 MySQL 的版本</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT VERSION();</span><br><span class="line">+<span class="comment">------------+</span></span><br><span class="line">| VERSION()  |</span><br><span class="line">+<span class="comment">------------+</span></span><br><span class="line">| 5.6.45-log |</span><br><span class="line">+<span class="comment">------------+</span></span><br><span class="line">1 row in <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br></pre></td></tr></table></figure></p></li><li><p>显示当前时间</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT NOW(); </span><br><span class="line">+<span class="comment">---------------------+</span></span><br><span class="line">| NOW()               |</span><br><span class="line">+<span class="comment">---------------------+</span></span><br><span class="line">+<span class="comment">---------------------+</span></span><br><span class="line">| 2021-01-13 20:05:07 |</span><br><span class="line">+<span class="comment">---------------------+</span></span><br><span class="line">1 row in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p></li><li><p>显示年月日</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT DAYOFMONTH(CURRENT_DATE);</span><br><span class="line">+<span class="comment">--------------------------+</span></span><br><span class="line">| DAYOFMONTH(CURRENT_DATE) |</span><br><span class="line">+<span class="comment">--------------------------+</span></span><br><span class="line">|                       13 |</span><br><span class="line">+<span class="comment">--------------------------+</span></span><br><span class="line">1 row in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; <span class="keyword">SELECT</span> <span class="keyword">MONTH</span>(<span class="keyword">CURRENT_DATE</span>);</span><br><span class="line">+<span class="comment">---------------------+</span></span><br><span class="line">| MONTH(CURRENT_DATE) |</span><br><span class="line">+<span class="comment">---------------------+</span></span><br><span class="line">|                   1 |</span><br><span class="line">+<span class="comment">---------------------+</span></span><br><span class="line">1 row in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; <span class="keyword">SELECT</span> <span class="keyword">YEAR</span>(<span class="keyword">CURRENT_DATE</span>);</span><br><span class="line">+<span class="comment">--------------------+</span></span><br><span class="line">| YEAR(CURRENT_DATE) |</span><br><span class="line">+<span class="comment">--------------------+</span></span><br><span class="line">|               2021 |</span><br><span class="line">+<span class="comment">--------------------+</span></span><br><span class="line">1 row in <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br></pre></td></tr></table></figure></p></li><li><p>显示字符串</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT &quot;welcome to my blog!&quot;; </span><br><span class="line">+<span class="comment">---------------------+</span></span><br><span class="line">| welcome to my blog! |</span><br><span class="line">+<span class="comment">---------------------+</span></span><br><span class="line">| welcome to my blog! |</span><br><span class="line">+<span class="comment">---------------------+</span></span><br><span class="line">1 row in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p></li><li><p>当计算器用</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> ((<span class="number">4</span> * <span class="number">4</span>) / <span class="number">10</span> ) + <span class="number">25</span>; </span><br><span class="line">+<span class="comment">----------------------+</span></span><br><span class="line">| ((4 * 4) / 10 ) + 25 |</span><br><span class="line">+<span class="comment">----------------------+</span></span><br><span class="line">|              26.6000 |</span><br><span class="line">+<span class="comment">----------------------+</span></span><br><span class="line">1 row in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p></li><li><p>串接字符串</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">CONCAT</span>(f_name, <span class="string">&quot; &quot;</span>, l_name) <span class="keyword">AS</span> <span class="keyword">Name</span> </span><br><span class="line"><span class="keyword">FROM</span> employee_data </span><br><span class="line"><span class="keyword">WHERE</span> title = <span class="string">&#x27;Marketing Executive&#x27;</span>; </span><br><span class="line">+<span class="comment">---------------+</span></span><br><span class="line">| Name          |</span><br><span class="line">+<span class="comment">---------------+</span></span><br><span class="line">| Monica Sehgal |</span><br><span class="line">| Hal Simlai    |</span><br><span class="line">| Joseph Irvine |</span><br><span class="line">+<span class="comment">---------------+</span></span><br><span class="line">3 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p> <strong>注意</strong>：这里用到 <code>CONCAT()</code> 函数，用来把字符串连接起来。另外，我们还用到 <code>AS</code> 给结果列 <code>CONCAT(f_name, &quot; &quot;, l_name)</code> 起了个假名。</p></li></ol><h2 id="数据表"><a href="#数据表" class="headerlink" title="数据表"></a>数据表</h2><h3 id="创建数据表"><a href="#创建数据表" class="headerlink" title="创建数据表"></a>创建数据表</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> &lt;表名&gt; (&lt;字段名<span class="number">1</span>&gt; &lt;类型<span class="number">1</span>&gt; , ... , &lt;字段名n&gt; &lt;类型n&gt;);</span><br></pre></td></tr></table></figure></p><p>以下为数据表的字段：</p><div class="table-container"><table><thead><tr><th style="text-align:center">字段名</th><th style="text-align:center">数字类型</th><th style="text-align:center">数据宽度</th><th style="text-align:center">是否为空</th><th style="text-align:center">是否主键</th><th style="text-align:center">自动增加</th><th style="text-align:center">默认值</th></tr></thead><tbody><tr><td style="text-align:center">id</td><td style="text-align:center">int</td><td style="text-align:center">4</td><td style="text-align:center">否</td><td style="text-align:center">PRIMARY KEY</td><td style="text-align:center">AUTO_INCREMENT</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">name</td><td style="text-align:center">char</td><td style="text-align:center">20</td><td style="text-align:center">否</td><td style="text-align:center">&nbsp;</td><td style="text-align:center">&nbsp;</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">sex</td><td style="text-align:center">int</td><td style="text-align:center">4</td><td style="text-align:center">否</td><td style="text-align:center">&nbsp;</td><td style="text-align:center">&nbsp;</td><td style="text-align:center">0</td></tr><tr><td style="text-align:center">degree</td><td style="text-align:center">double</td><td style="text-align:center">16</td><td style="text-align:center">是</td><td style="text-align:center">&nbsp;</td><td style="text-align:center">&nbsp;</td><td style="text-align:center">&nbsp;</td></tr></tbody></table></div><p>执行的 SQL 语句为：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE TABLE MyClass(</span><br><span class="line">     &gt; id int(4) NOT NULL PRIMARY KEY AUTO_INCREMENT,</span><br><span class="line">     &gt; name char(20) NOT NULL,</span><br><span class="line">     &gt; sex int(4) NOT NULL DEFAULT &#x27;0&#x27;,</span><br><span class="line">     &gt; degree double(16, 2));</span><br></pre></td></tr></table></figure></p><h3 id="删除数据表"><a href="#删除数据表" class="headerlink" title="删除数据表"></a>删除数据表</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> &lt;表名&gt;;</span><br></pre></td></tr></table></figure></p><p>例如：删除表名为 MyClass 的表</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> MyClass;</span><br></pre></td></tr></table></figure></p><p><strong>注意</strong>：</p><ul><li><code>DROP TABLE</code> 用于取消一个或多个表。但必须有表的 <code>DROP</code> 权限。所有的表数据和表定义会被取消，所以使用本语句要小心。</li><li>对于一个带分区的表，<code>DROP TABLE</code> 会永久性地取消表定义，取消各分区，并取消储存在这些分区中的所有数据。<code>DROP TABLE</code> 还会取消与被取消的表有关联的分区定义（<code>.par</code>）文件。</li><li>对于不存在的表，使用 <code>IF EXISTS</code> 用于防止错误发生。当使用 <code>IF EXISTS</code> 时，对于每个不存在的表，会生成一个 <code>NOTE</code>。</li><li><code>RESTRICT</code> 和 <code>CASCADE</code> 可以使分区更容易。但目前，<code>RESTRICT</code> 和 <code>CASCADE</code> 不起作用。</li></ul><h3 id="插入数据"><a href="#插入数据" class="headerlink" title="插入数据"></a>插入数据</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> &lt;表名&gt; (&lt;字段名<span class="number">1</span>&gt;, ..., &lt;字段名n &gt;) <span class="keyword">VALUES</span> (值<span class="number">1</span>, ...,  值n );</span><br></pre></td></tr></table></figure></p><p>例如：往表 <code>MyClass</code> 中插入二条记录, 这二条记录表示：编号为 <code>1</code> 的名为 <code>Tom</code> 的成绩为 <code>96.45</code>, 编号为 <code>2</code> 的名为 <code>Joan</code> 的成绩为 <code>82.99</code>，编号为 <code>3</code> 的名为 <code>Wang</code> 的成绩为 <code>96.5</code>。</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; INSERT INTO MyClass(id, name, degree) VALUES (1, &#x27;Tom&#x27;, 96.45),(2, &#x27;Joan&#x27;, 82.99), (3, &#x27;Wang&#x27;, 96.59);</span><br></pre></td></tr></table></figure></p><h3 id="查询表中的数据"><a href="#查询表中的数据" class="headerlink" title="查询表中的数据"></a>查询表中的数据</h3><ol><li><p>查询所有行命令：</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> &lt;字段<span class="number">1</span>, 字段<span class="number">2</span>, ...&gt; <span class="keyword">FROM</span> &lt;表名&gt; <span class="keyword">WHERE</span> &lt;表达式&gt;;</span><br></pre></td></tr></table></figure></p><p> <code>SELECT</code> 一般配合 <code>WHERE</code> 使用，以查询更精确更复杂的数据。</p><p> 例如：查看表 <code>MyClass</code> 中 <code>degree</code> 大于 <code>90</code> 的数据。</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM MyClass WHERE degree &gt; 90;</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">| id | name | sex | degree |</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">|  1 | Tom  |   0 |  96.45 |</span><br><span class="line">|  3 | Wang |   0 |  96.59 |</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">3 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p></li><li><p>查询前几行数据 </p><p> 例如：查看表 <code>MyClass</code> 中前 <code>2</code> 行数据</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM MyClass ORDER BY id LIMIT 0, 2;</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">| id | name | sex | degree |</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">|  1 | Tom  |   0 |  96.45 |</span><br><span class="line">|  2 | Joan |   0 |  82.99 |</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p></li></ol><h3 id="删除表中数据"><a href="#删除表中数据" class="headerlink" title="删除表中数据"></a>删除表中数据</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> &lt;表名&gt; <span class="keyword">WHERE</span> &lt;表达式&gt;</span><br></pre></td></tr></table></figure></p><p>例如：删除表 <code>MyClass</code> 中编号为 <code>1</code> 的记录</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM MyClass;</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">| id | name | sex | degree |</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">|  1 | Tom  |   0 |  96.45 |</span><br><span class="line">|  2 | Joan |   0 |  82.99 |</span><br><span class="line">|  3 | Wang |   0 |  96.59 |</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">3 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; <span class="keyword">DELETE</span> <span class="keyword">FROM</span> MyClass <span class="keyword">WHERE</span> <span class="keyword">id</span> = <span class="number">1</span>;</span><br><span class="line">Query OK, 1 row affected (0.00 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; SELECT * FROM MyClass;</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">| id | name | sex | degree |</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">|  2 | Joan |   0 |  82.99 |</span><br><span class="line">|  3 | Wang |   0 |  96.59 |</span><br><span class="line">+<span class="comment">----+------+-----+--------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><h3 id="修改表中数据"><a href="#修改表中数据" class="headerlink" title="修改表中数据"></a>修改表中数据</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">UPDATE</span> &lt;表名&gt; <span class="keyword">SET</span> &lt;字段&gt; = &lt;新值&gt;, ... <span class="keyword">WHERE</span> &lt;表达式&gt;</span><br></pre></td></tr></table></figure></p><p>例如：修改表 <code>MyClass</code> 中 <code>id</code> 为 <code>2</code> 的 <code>name</code> 为 <code>Mary</code>。</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; UPDATE MyClass SET name = &#x27;Mary&#x27; WHERE id = 2;</span><br></pre></td></tr></table></figure></p><ol><li><p>单表的 UPDATE 语句：</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">UPDATE</span> [<span class="keyword">LOW_PRIORITY</span>] [<span class="keyword">IGNORE</span>] tbl_name </span><br><span class="line"><span class="keyword">SET</span> col_name1=expr1 [, col_name2=expr2 ...] </span><br><span class="line">[<span class="keyword">WHERE</span> where_definition] </span><br><span class="line">[<span class="keyword">ORDER</span> <span class="keyword">BY</span> ...] </span><br><span class="line">[<span class="keyword">LIMIT</span> <span class="keyword">row_count</span>]</span><br></pre></td></tr></table></figure></p></li><li><p>多表的 UPDATE 语句：</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">UPDATE</span> [<span class="keyword">LOW_PRIORITY</span>] [<span class="keyword">IGNORE</span>] table_references </span><br><span class="line"><span class="keyword">SET</span> col_name1=expr1 [, col_name2=expr2 ...] </span><br><span class="line">[<span class="keyword">WHERE</span> where_definition]</span><br></pre></td></tr></table></figure></p></li></ol><p><strong>注意</strong>：<code>UPDATE</code> 语法可以用新值更新原有表行中的各列。SET 子句指示要修改哪些列和要给予哪些值。<code>WHERE</code> 子句指定应更新哪些行。如果没有 <code>WHERE</code> 子句，则更新所有的行。如果指定了 <code>ORDER BY</code> 子句，则按照被指定的顺序对行进行更新。<code>LIMIT</code> 子句用于给定一个限值，限制可以被更新的行的数目。</p><h3 id="修改数据表"><a href="#修改数据表" class="headerlink" title="修改数据表"></a>修改数据表</h3><ol><li><p>增加字段</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> &lt;表名&gt; <span class="keyword">ADD</span> &lt;字段&gt; &lt;类型&gt; &lt;其他&gt;; </span><br></pre></td></tr></table></figure></p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE MyClass ADD passtest int(4) default &#x27;0&#x27;</span><br></pre></td></tr></table></figure></p></li><li><p>增加索引</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> &lt;表名&gt; <span class="keyword">ADD</span> <span class="keyword">INDEX</span> &lt;索引名&gt;(&lt;字段名<span class="number">1</span>&gt;, &lt;字段名<span class="number">2</span>&gt;, ...)</span><br></pre></td></tr></table></figure></p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE employee ADD INDEX emp_name(name);</span><br></pre></td></tr></table></figure></p></li><li><p>增加主关键字的索引</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> &lt;表名&gt; <span class="keyword">ADD</span> PRIMARY <span class="keyword">KEY</span> (&lt;字段名&gt;);</span><br></pre></td></tr></table></figure></p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE employee ADD PRIMARY KEY (id);</span><br></pre></td></tr></table></figure></p></li><li><p>增加唯一索引</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> &lt;表名&gt; <span class="keyword">ADD</span> <span class="keyword">UNIQUE</span> &lt;索引名&gt;(&lt;字段名&gt;);</span><br></pre></td></tr></table></figure></p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE employee ADD UNIQUE emp_name2(cardnumber);</span><br></pre></td></tr></table></figure></p></li><li><p>增加全文索引</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> &lt;表名&gt; <span class="keyword">ADD</span> FULLTEXT &lt;索引名&gt;(&lt;字段名&gt;);</span><br></pre></td></tr></table></figure></p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE employee ADD FULLTEXT emp_name3(name);</span><br></pre></td></tr></table></figure></p><p> <strong>注意</strong>：全文索引只能用于数据库引擎为 <code>MyISAM</code> 的数据表，只适用于 <code>VARCHAR</code> 和 <code>TEXT</code> 类型的字段，此外，MySQL 自带的全文索引只能对英文进行全文检索，目前无法对中文进行全文检索。</p></li><li><p>删除某个索引</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> &lt;表名&gt; <span class="keyword">DROP</span> <span class="keyword">INDEX</span> &lt;索引名&gt;;</span><br></pre></td></tr></table></figure></p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt;ALTER TABLE employee DROP INDEX emp_name;</span><br></pre></td></tr></table></figure></p></li><li><p>修改原字段名称及类型</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> &lt;表名&gt; <span class="keyword">CHANGE</span> &lt;旧字段名&gt; &lt;新字段名&gt; &lt;新字段类型&gt; &lt;其他&gt;;</span><br></pre></td></tr></table></figure></p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> employee <span class="keyword">CHANGE</span> <span class="keyword">name</span> full_name <span class="built_in">char</span>(<span class="number">10</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>;</span><br></pre></td></tr></table></figure></p></li><li><p>删除字段</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> &lt;表名&gt; <span class="keyword">DROP</span> &lt;字段名&gt;;</span><br></pre></td></tr></table></figure></p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> employee <span class="keyword">DROP</span> address;</span><br></pre></td></tr></table></figure></p></li><li><p>修改表名</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">RENAME</span> <span class="keyword">TABLE</span> &lt;原表名&gt; <span class="keyword">TO</span> &lt;新表名&gt;;</span><br></pre></td></tr></table></figure></p><p> <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; RENAME TABLE MyClass TO YouClass;</span><br></pre></td></tr></table></figure></p><p> <strong>注意</strong>：</p><ul><li>当你执行 <code>RENAME</code> 时，你不能有任何锁定的表或活动的事务。你同样也必须有对原初表的 <code>ALTER</code> 和 <code>DROP</code> 权限，以及对新表的 <code>CREATE</code> 和 <code>INSERT</code> 权限。</li><li>如果在多表更名中，MySQL 遭遇到任何错误，它将对所有被更名的表进行倒退更名，将每件事务退回到最初状态。</li><li><code>RENAME TABLE</code> 在 MySQL 3.23.23 中被加入。</li></ul></li></ol><h2 id="备份数据库"><a href="#备份数据库" class="headerlink" title="备份数据库"></a>备份数据库</h2><p>命令在 <code>DOS</code> 的 <code>\mysql\bin</code> 目录下执行，导出文件默认是存在 <code>mysql\bin</code> 目录下。</p><h3 id="导出整个数据库"><a href="#导出整个数据库" class="headerlink" title="导出整个数据库"></a>导出整个数据库</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mysqldump -u 用户名 -p&#x27;密码&#x27; 数据库名 &gt; 导出的文件名.sql</span><br><span class="line"></span><br><span class="line">mysqldump -u 用户名 -p 数据库名 &gt; 导出的文件名.sql</span><br><span class="line">Enter password: ******</span><br><span class="line"></span><br><span class="line">mysqldump -u root -p database_name &gt; outfile.sql</span><br><span class="line">Enter password: ******</span><br></pre></td></tr></table></figure></p><h3 id="导出所有数据库"><a href="#导出所有数据库" class="headerlink" title="导出所有数据库"></a>导出所有数据库</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysqldump  -uroot -p --all-databases &gt; outfile.sql</span><br><span class="line">mysqldump  -uroot -p -A &gt; outfile.sql</span><br></pre></td></tr></table></figure></p><h3 id="导出一个数据库结构"><a href="#导出一个数据库结构" class="headerlink" title="导出一个数据库结构"></a>导出一个数据库结构</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqldump -u root -p -d –add-drop-table database_name &gt; outfile.sql</span><br></pre></td></tr></table></figure></p><p><code>-d</code> 没有数据, <code>–add-drop-table</code>在每个 <code>CREATE</code> 语句之前增加一个 <code>DROP TABLE</code></p><h3 id="带语言参数导出"><a href="#带语言参数导出" class="headerlink" title="带语言参数导出"></a>带语言参数导出</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqldump -uroot -p –default-character-set=latin1 –set-charset=gbk –skip-opt database_name &gt; outfile.sql</span><br></pre></td></tr></table></figure></p><h3 id="导出数据表"><a href="#导出数据表" class="headerlink" title="导出数据表"></a>导出数据表</h3><ol><li><p>导出数据表和表结构</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqldump -u root -h 登录主机 -p&#x27;密码&#x27; -P 端口号 数据库名 表名 &gt; 文件名.sql</span><br></pre></td></tr></table></figure></p><p> 将特定数据库特定表中的数据和表格结构和数据全部返回。</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqldump -u root -h localhost -p -P3306 database_name table &gt; table.sql</span><br></pre></td></tr></table></figure></p></li><li><p>导出表结构却不导出表数据</p><p> 只返回特定数据库特定表格的表格结构，不返回数据，添加 <code>-d</code> 命令参数。</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqldump -u root -h localhost -p -P3306 -d database_name table &gt; table.sql</span><br></pre></td></tr></table></figure></p></li><li><p>导出表结构和满足 <code>WHERE</code> 条件的表数据</p><p> 只返回特定数据库中特定表的表格结构和满足特定条件的数据。</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqldump -u root -h localhost -p -P3306 database_name table -where=&quot;time&gt;&#x27;2017-01-01&#x27;&quot; &gt; table.sql</span><br></pre></td></tr></table></figure></p></li><li><p>导出数据却不导出表结构</p><p> 只返回特定数据库中特定表格的数据，不返回表格结构，添加 <code>-t</code> 命令参数。</p><p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqldump -u root -h localhost -p -t -P3306 database_name table &gt; table.sql</span><br></pre></td></tr></table></figure></p></li></ol><h3 id="导入-SQL-文件"><a href="#导入-SQL-文件" class="headerlink" title="导入 SQL 文件"></a>导入 SQL 文件</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source database.sql;</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/MySQL/">MySQL</category>
      
      
      <comments>https://blog.eurkon.com/post/4bd4f98e.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>MySQL 教程</title>
      <link>https://blog.eurkon.com/post/7f59cefa.html</link>
      <guid>https://blog.eurkon.com/post/7f59cefa.html</guid>
      <pubDate>Sat, 09 Jan 2021 02:35:46 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;术语&quot;&gt;&lt;a href=&quot;#术语&quot; class=&quot;headerlink&quot; title=&quot;术语&quot;&gt;&lt;/a&gt;术语&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;数据库: 数据库是一些关联表的集合。&lt;/li&gt;
&lt;li&gt;数据表: 表是数据的矩阵。在一个数据库中的表看起来像一个简单的电子表格。</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="术语"><a href="#术语" class="headerlink" title="术语"></a>术语</h2><ul><li>数据库: 数据库是一些关联表的集合。</li><li>数据表: 表是数据的矩阵。在一个数据库中的表看起来像一个简单的电子表格。</li><li>列: 一列（数据元素）包含了相同类型的数据，例如邮政编码的数据。</li><li>行：一行（元组，或记录）是一组相关的数据，例如一条用户订阅的数据。</li><li>冗余：存储两倍数据，冗余降低了性能，但提高了数据的安全性。</li><li>主键：主键是唯一的。一个数据表中只能包含一个主键。你可以使用主键来查询数据。</li><li>外键：外键用于关联两个表。</li><li>复合键：复合键（组合键）将多个列作为一个索引键，一般用于复合索引。</li><li>索引：使用索引可快速访问数据库表中的特定信息。索引是对数据库表中一列或多列的值进行排序的一种结构。类似于书籍的目录。</li><li>参照完整性: 参照的完整性要求关系中不允许引用不存在的实体。与实体完整性是关系模型必须满足的完整性约束条件，目的是保证数据的一致性。</li></ul><h2 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h2><p>MySQL 是一个关系型数据库管理系统（RDBMS），由瑞典 MySQL AB 公司开发，目前属于 Oracle 公司。MySQL 是一种关联数据库管理系统，关联数据库将数据保存在不同的表中，而不是将所有数据放在一个大仓库内，这样就增加了速度并提高了灵活性。</p><ul><li>MySQL 是开源的，所以你不需要支付额外的费用。</li><li>MySQL 支持大型的数据库。可以处理拥有上千万条记录的大型数据库。</li><li>MySQL 使用标准的 SQL 数据语言形式。</li><li>MySQL 可以运行于多个系统上，并且支持多种语言。这些编程语言包括 C、C++、Python、Java、Perl、PHP、Eiffel、Ruby 和 Tcl 等。</li><li>MySQL 对 PHP 有很好的支持，PHP 是目前最流行的 Web 开发语言。</li><li>MySQL 支持大型数据库，支持 5000 万条记录的数据仓库，32 位系统表文件最大可支持 4GB，64 位系统支持最大的表文件为 8TB。</li><li>MySQL 是可以定制的，采用了 GPL 协议，你可以修改源码来开发自己的 MySQL 系统。</li></ul><h2 id="连接"><a href="#连接" class="headerlink" title="连接"></a>连接</h2><p>您可以使用 MySQL 二进制方式进入到 <code>mysql</code> 命令提示符下来连接 MySQL 数据库。</p><p>以下是在命令提示窗口中连接 MySQL 服务器的简单实例：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">[root@host]#</span><span class="bash"> mysql -u root -p</span></span><br><span class="line">Enter password:******</span><br></pre></td></tr></table></figure></p><p>在登录成功后会出现 <code>mysql&gt;</code> 命令提示窗口，你可以在上面执行任何 SQL 语句。</p><p>以上命令执行后，登录成功输出结果如下:</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Welcome to the MySQL monitor.  Commands end with ; or \g.</span><br><span class="line">Your MySQL connection id is 2854760 to server version: 5.0.9</span><br><span class="line"></span><br><span class="line">Type &#x27;help;&#x27; or &#x27;\h&#x27; for help. Type &#x27;\c&#x27; to clear the buffer.</span><br></pre></td></tr></table></figure></p><p>在以上实例中，我们使用了 <code>root</code> 用户登录到 MySQL 服务器，当然你也可以使用其他 MySQL 用户登录。</p><p>如果用户权限足够，任何用户都可以在 <code>mysql</code> 的命令提示窗口中进行 SQL 操作。</p><p>退出 <code>mysql&gt;</code> 命令提示窗口可以使用 <code>exit</code> 命令，如下所示：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; exit</span><br><span class="line">Bye</span><br></pre></td></tr></table></figure></p><h2 id="创建数据库"><a href="#创建数据库" class="headerlink" title="创建数据库"></a>创建数据库</h2><p>我们可以在登陆 MySQL 服务后，使用 <code>CREATE DATABASE</code> 命令创建数据库，语法如下：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">DATABASE</span> 数据库名;</span><br></pre></td></tr></table></figure></p><h3 id="使用-CREATE-DATABASE-命令"><a href="#使用-CREATE-DATABASE-命令" class="headerlink" title="使用 CREATE DATABASE 命令"></a>使用 CREATE DATABASE 命令</h3><p>以下命令简单的演示了创建数据库的过程，数据名为 <code>TestDB</code>:</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">[root@host]#</span><span class="bash"> mysql -u root -p</span></span><br><span class="line">Enter password:******  # 登录后进入终端</span><br></pre></td></tr></table></figure></p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE DATABASE TestDB;</span><br></pre></td></tr></table></figure></p><h3 id="使用-mysqladmin-创建数据库"><a href="#使用-mysqladmin-创建数据库" class="headerlink" title="使用 mysqladmin 创建数据库"></a>使用 mysqladmin 创建数据库</h3><p>使用普通用户，你可能需要特定的权限来创建或者删除 MySQL 数据库。</p><p>所以我们这边使用 <code>root</code> 用户登录，<code>root</code> 用户拥有最高权限，可以使用 <code>mysql mysqladmin</code> 命令来创建数据库。</p><p>以下命令简单的演示了创建数据库的过程，数据名为 TestDB:</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">[root@host]#</span><span class="bash"> mysqladmin -u root -p CREATE TestDB</span></span><br><span class="line">Enter password:******</span><br></pre></td></tr></table></figure></p><p>以上命令执行成功后会创建 MySQL 数据库 TestDB。</p><h2 id="删除数据库"><a href="#删除数据库" class="headerlink" title="删除数据库"></a>删除数据库</h2><p>使用普通用户登陆 MySQL 服务器，你可能需要特定的权限来创建或者删除 MySQL 数据库，所以我们这边使用 root 用户登录，root 用户拥有最高权限。</p><p>在删除数据库过程中，务必要十分谨慎，因为在执行删除命令后，所有数据将会消失。</p><h3 id="使用-DROP-DATABASE-命令"><a href="#使用-DROP-DATABASE-命令" class="headerlink" title="使用 DROP DATABASE 命令"></a>使用 DROP DATABASE 命令</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">DATABASE</span> &lt;数据库名&gt;;</span><br></pre></td></tr></table></figure></p><p>例如删除名为 <code>TestDB</code> 的数据库：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; DROP DATABASE TestDB;</span><br></pre></td></tr></table></figure></p><h3 id="使用-mysqladmin-删除数据库"><a href="#使用-mysqladmin-删除数据库" class="headerlink" title="使用 mysqladmin 删除数据库"></a>使用 mysqladmin 删除数据库</h3><p>你也可以使用 <code>mysqladmin</code> 命令在终端来执行删除命令。</p><p>以下实例删除数据库 TestDB：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@host]<span class="comment"># mysqladmin -u root -p DROP TestDB</span></span><br><span class="line">Enter password:******</span><br></pre></td></tr></table></figure></p><p>执行以上删除数据库命令后，会出现一个提示框，来确认是否真的删除数据库：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Dropping the database is potentially a very bad thing to do.</span><br><span class="line">Any data stored in the database will be destroyed.</span><br><span class="line"></span><br><span class="line">Do you really want to drop the &#x27;TestDB&#x27; database [y/N] y</span><br><span class="line">Database &quot;TestDB&quot; dropped</span><br></pre></td></tr></table></figure></p><h2 id="选择数据库"><a href="#选择数据库" class="headerlink" title="选择数据库"></a>选择数据库</h2><p>在你连接到 MySQL 数据库后，可能有多个可以操作的数据库，所以你需要选择你要操作的数据库。</p><p>从命令提示窗口中选择 MySQL 数据库</p><p>在 <code>mysql&gt;</code> 提示窗口中可以很简单的选择特定的数据库。你可以使用 SQL 命令来选择指定的数据库。</p><p>以下实例选取了数据库 <code>TestDB</code>:</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@host]<span class="comment"># mysql -u root -p</span></span><br><span class="line">Enter password:******</span><br><span class="line">mysql&gt; use TestDB;</span><br><span class="line">Database changed</span><br><span class="line">mysql&gt;</span><br></pre></td></tr></table></figure></p><p>执行以上命令后，你就已经成功选择了 TestDB 数据库，在后续的操作中都会在 TestDB 数据库中执行。</p><p>注意:所有的数据库名，表名，表字段都是区分大小写的。所以你在使用 SQL 命令时需要输入正确的名称。</p><h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><p>MySQL 中定义数据字段的类型对你数据库的优化是非常重要的。</p><p>MySQL 支持多种类型，大致可以分为三类：数值、日期/时间和字符串(字符)类型。</p><h3 id="数值类型"><a href="#数值类型" class="headerlink" title="数值类型"></a>数值类型</h3><p>MySQL 支持所有标准 SQL 数值数据类型。</p><p>这些类型包括严格数值数据类型（<code>INTEGER</code>、<code>SMALLINT</code>、<code>DECIMAL</code> 和 <code>NUMERIC</code>），以及近似数值数据类型（<code>FLOAT</code>、<code>REAL</code> 和 <code>DOUBLE PRECISION</code>）。</p><p>关键字 <code>INT</code> 是 <code>INTEGER</code> 的同义词，关键字 <code>DEC</code> 是 <code>DECIMAL</code> 的同义词。</p><p>BIT 数据类型保存位字段值，并且支持 MyISAM、MEMORY、InnoDB 和 BDB 表。</p><p>作为 SQL 标准的扩展，MySQL 也支持整数类型 <code>TINYINT</code>、<code>MEDIUMINT</code> 和 <code>BIGINT</code>。下面的表显示了需要的每个整数类型的存储和范围。</p><div class="table-container"><table><thead><tr><th style="text-align:center">类型</th><th style="text-align:center">大小</th><th style="text-align:center">范围（有符号）</th><th style="text-align:center">范围（无符号）</th><th style="text-align:center">用途</th></tr></thead><tbody><tr><td style="text-align:center">TINYINT</td><td style="text-align:center">1 byte</td><td style="text-align:center">(-128，127)</td><td style="text-align:center">(0，255)</td><td style="text-align:center">小整数值</td></tr><tr><td style="text-align:center">SMALLINT</td><td style="text-align:center">2 bytes</td><td style="text-align:center">(-32 768，32 767)</td><td style="text-align:center">(0，65 535)</td><td style="text-align:center">大整数值</td></tr><tr><td style="text-align:center">MEDIUMINT</td><td style="text-align:center">3 bytes</td><td style="text-align:center">(-8 388 608，8 388 607)</td><td style="text-align:center">(0，16 777 215)</td><td style="text-align:center">大整数值</td></tr><tr><td style="text-align:center">INT 或<br>INTEGER</td><td style="text-align:center">4 bytes</td><td style="text-align:center">(-2 147 483 648，2 147 483 647)</td><td style="text-align:center">(0，4 294 967 295)</td><td style="text-align:center">大整数值</td></tr><tr><td style="text-align:center">BIGINT</td><td style="text-align:center">8 bytes</td><td style="text-align:center">(-9 223 372 036 854 775 808，<br>9 223 372 036 854 775 807)</td><td style="text-align:center">(0，<br>18 446 744 073 709 551 615)</td><td style="text-align:center">极大整数值</td></tr><tr><td style="text-align:center">FLOAT</td><td style="text-align:center">4 bytes</td><td style="text-align:center">(-3.402 823 466 E+38，<br>-1.175 494 351 E-38)，<br>0，<br>(1.175 494 351 E-38，<br>3.402 823 466 351 E+38)</td><td style="text-align:center">0，<br>(1.175 494 351 E-38，<br>3.402 823 466 E+38)</td><td style="text-align:center">单精度浮点数值</td></tr><tr><td style="text-align:center">DOUBLE</td><td style="text-align:center">8 bytes</td><td style="text-align:center">(-1.797 693 134 862 315 7 E+308，<br>-2.225 073 858 507 201 4 E-308)，<br>0，<br>(2.225 073 858 507 201 4 E-308，<br>1.797 693 134 862 315 7 E+308)</td><td style="text-align:center">0，<br>(2.225 073 858 507 201 4 E-308，<br>1.797 693 134 862 315 7 E+308)</td><td style="text-align:center">双精度浮点数值</td></tr><tr><td style="text-align:center">DECIMAL</td><td style="text-align:center">DECIMAL(M, D)，<br>如果 M&gt;D，<br>为 M+2 ，否则为 D+2</td><td style="text-align:center">依赖于 M 和 D 的值</td><td style="text-align:center">依赖于 M 和 D 的值</td><td style="text-align:center">小数值</td></tr></tbody></table></div><h3 id="日期和时间类型"><a href="#日期和时间类型" class="headerlink" title="日期和时间类型"></a>日期和时间类型</h3><p>表示时间值的日期和时间类型为 <code>DATETIME</code>、<code>DATE</code>、<code>TIMESTAMP</code>、<code>TIME</code> 和 <code>YEAR</code>。</p><p>每个时间类型有一个有效值范围和一个“零”值，当指定不合法的 MySQL 不能表示的值时使用“零”值。</p><p><code>TIMESTAMP</code> 类型有专有的自动更新特性，将在后面描述。</p><div class="table-container"><table><thead><tr><th style="text-align:center">类型</th><th style="text-align:center">大小</th><th style="text-align:center">范围</th><th style="text-align:center">格式</th><th style="text-align:center">用途</th></tr></thead><tbody><tr><td style="text-align:center">DATE</td><td style="text-align:center">3 bytes</td><td style="text-align:center">1000-01-01 /<br> 9999-12-31</td><td style="text-align:center">YYYY-MM-DD</td><td style="text-align:center">日期值</td></tr><tr><td style="text-align:center">TIME</td><td style="text-align:center">3 bytes</td><td style="text-align:center">&#39;-838:59:59&#39; /<br>&#39;838:59:59&#39;</td><td style="text-align:center">HH:MM:SS</td><td style="text-align:center">时间值或持续时间</td></tr><tr><td style="text-align:center">YEAR</td><td style="text-align:center">1 bytes</td><td style="text-align:center">1901 / 2155</td><td style="text-align:center">YYYY</td><td style="text-align:center">年份值</td></tr><tr><td style="text-align:center">DATETIME</td><td style="text-align:center">8 bytes</td><td style="text-align:center">1000-01-01 00:00:00 /<br>9999-12-31 23:59:59</td><td style="text-align:center">YYYY-MM-DD HH:MM:SS</td><td style="text-align:center">混合日期和时间值</td></tr><tr><td style="text-align:center">TIMESTAMP</td><td style="text-align:center">4 bytes</td><td style="text-align:center">1970-01-01 00:00:00 /<br>结束时间是第 2147483647 秒，<br>北京时间 2038-1-19 11:14:07，<br>格林尼治时间 2038-1-19 03:14:07</td><td style="text-align:center">YYYYMMDD HHMMSS</td><td style="text-align:center">混合日期和时间值，时间戳</td></tr></tbody></table></div><h3 id="字符串类型"><a href="#字符串类型" class="headerlink" title="字符串类型"></a>字符串类型</h3><p>字符串类型指 <code>CHAR</code>、<code>VARCHAR</code>、<code>BINARY</code>、<code>VARBINARY</code>、<code>BLOB</code>、<code>TEXT</code>、<code>ENUM</code> 和 <code>SET</code>。该节描述了这些类型如何工作以及如何在查询中使用这些类型。</p><div class="table-container"><table><thead><tr><th style="text-align:center">类型</th><th style="text-align:center">大小</th><th style="text-align:center">用途</th></tr></thead><tbody><tr><td style="text-align:center">CHAR</td><td style="text-align:center">0-255 bytes</td><td style="text-align:center">定长字符串</td></tr><tr><td style="text-align:center">VARCHAR</td><td style="text-align:center">0-65535 bytes</td><td style="text-align:center">变长字符串</td></tr><tr><td style="text-align:center">TINYBLOB</td><td style="text-align:center">0-255 bytes</td><td style="text-align:center">不超过 255 个字符的二进制字符串</td></tr><tr><td style="text-align:center">TINYTEXT</td><td style="text-align:center">0-255 bytes</td><td style="text-align:center">短文本字符串</td></tr><tr><td style="text-align:center">BLOB</td><td style="text-align:center">0-65 535 bytes</td><td style="text-align:center">二进制形式的长文本数据</td></tr><tr><td style="text-align:center">TEXT</td><td style="text-align:center">0-65 535 bytes</td><td style="text-align:center">长文本数据</td></tr><tr><td style="text-align:center">MEDIUMBLOB</td><td style="text-align:center">0-16 777 215 bytes</td><td style="text-align:center">二进制形式的中等长度文本数据</td></tr><tr><td style="text-align:center">MEDIUMTEXT</td><td style="text-align:center">0-16 777 215 bytes</td><td style="text-align:center">中等长度文本数据</td></tr><tr><td style="text-align:center">LONGBLOB</td><td style="text-align:center">0-4 294 967 295 bytes</td><td style="text-align:center">二进制形式的极大文本数据</td></tr><tr><td style="text-align:center">LONGTEXT</td><td style="text-align:center">0-4 294 967 295 bytes</td><td style="text-align:center">极大文本数据</td></tr></tbody></table></div><p><strong>注意：</strong></p><ul><li><p><code>char(n)</code> 和 <code>varchar(n)</code> 中括号中 <code>n</code> 代表字符的个数，并不代表字节个数，比如 <code>CHAR(30)</code> 就可以存储 30 个字符。</p></li><li><p><code>CHAR</code> 和 <code>VARCHAR</code> 类型类似，但它们保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面也不同。在存储或检索过程中不进行大小写转换。</p></li><li><p><code>BINARY</code> 和 <code>VARBINARY</code> 类似于 <code>CHAR</code> 和 <code>VARCHAR</code>，不同的是它们包含二进制字符串而不要非二进制字符串。也就是说，它们包含字节字符串而不是字符字符串。这说明它们没有字符集，并且排序和比较基于列值字节的数值值。</p></li><li><p><code>BLOB</code> 是一个二进制大对象，可以容纳可变数量的数据。有 4 种 <code>BLOB</code> 类型：<code>TINYBLOB</code>、<code>BLOB</code>、<code>MEDIUMBLOB</code> 和 <code>LONGBLOB</code>。它们区别在于可容纳存储范围不同。</p></li><li><p>有 4 种 <code>TEXT</code> 类型：<code>TINYTEXT</code>、<code>TEXT</code>、<code>MEDIUMTEXT</code> 和 <code>LONGTEXT</code>。对应的这 4 种 <code>BLOB</code> 类型，可存储的最大长度不同，可根据实际情况选择。</p></li></ul><h2 id="创建数据表"><a href="#创建数据表" class="headerlink" title="创建数据表"></a>创建数据表</h2><p>创建 MySQL 数据表需要以下信息：</p><ul><li>表名</li><li>表字段名</li><li>定义每个表字段</li></ul><h3 id="CREATE-TABLE-命令"><a href="#CREATE-TABLE-命令" class="headerlink" title="CREATE TABLE 命令"></a>CREATE TABLE 命令</h3><p>以下为创建 MySQL 数据表的 SQL 通用语法：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> table_name (column_name column_type);</span><br></pre></td></tr></table></figure></p><p>以下例子中我们将在 <code>TestDB</code> 数据库中创建数据表 <code>tbl</code>：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="keyword">IF</span> <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> <span class="string">`tbl`</span>(</span><br><span class="line">   <span class="string">`id`</span> <span class="built_in">INT</span> <span class="keyword">UNSIGNED</span> AUTO_INCREMENT,</span><br><span class="line">   <span class="string">`title`</span> <span class="built_in">VARCHAR</span>(<span class="number">100</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">   <span class="string">`author`</span> <span class="built_in">VARCHAR</span>(<span class="number">40</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">   <span class="string">`submission_date`</span> <span class="built_in">DATE</span>,</span><br><span class="line">   PRIMARY <span class="keyword">KEY</span> ( <span class="string">`id`</span> )</span><br><span class="line">)<span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span>=utf8;</span><br></pre></td></tr></table></figure></p><p><strong>实例解析：</strong></p><ul><li>如果你不想字段为 <code>NULL</code> 可以设置字段的属性为 <code>NOT NULL</code>， 在操作数据库时如果输入该字段的数据为 <code>NULL</code>，就会报错。</li><li><code>AUTO_INCREMENT</code> 定义列为自增的属性，一般用于主键，数值会自动加 1。</li><li><code>PRIMARY KEY</code> 关键字用于定义列为主键。 您可以使用多列来定义主键，列间以逗号分隔。</li><li><code>ENGINE</code> 设置存储引擎，<code>CHARSET</code> 设置编码。</li></ul><h3 id="在命令提示窗口中创建表"><a href="#在命令提示窗口中创建表" class="headerlink" title="在命令提示窗口中创建表"></a>在命令提示窗口中创建表</h3><p>通过 <code>mysql&gt;</code> 命令窗口可以很简单的创建 MySQL 数据表。你可以使用 SQL 语句 <code>CREATE TABLE</code> 来创建数据表。</p><p>以下为创建数据表 tbl 实例:</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE TABLE tbl(</span><br><span class="line">    -&gt; id INT NOT NULL AUTO_INCREMENT,</span><br><span class="line">    -&gt; title VARCHAR(100) NOT NULL,</span><br><span class="line">    -&gt; author VARCHAR(40) NOT NULL,</span><br><span class="line">    -&gt; submission_date DATE,</span><br><span class="line">    -&gt; PRIMARY KEY ( id )</span><br><span class="line">    -&gt; )ENGINE=InnoDB DEFAULT CHARSET=utf8;</span><br><span class="line">Query OK, 0 rows affected (0.16 sec)</span><br></pre></td></tr></table></figure></p><p><strong>注意</strong>：MySQL 命令终止符为分号 <code>;</code> 。</p><h2 id="删除数据表"><a href="#删除数据表" class="headerlink" title="删除数据表"></a>删除数据表</h2><p>MySQL 中删除数据表是非常容易操作的， 但是你再进行删除表操作时要非常小心，因为执行删除命令后所有数据都会消失。</p><h3 id="DROP-TABLE-命令"><a href="#DROP-TABLE-命令" class="headerlink" title="DROP TABLE 命令"></a>DROP TABLE 命令</h3><p>以下为删除 MySQL 数据表的通用语法：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> table_name ;</span><br></pre></td></tr></table></figure></p><h3 id="在命令提示窗口中删除数据表"><a href="#在命令提示窗口中删除数据表" class="headerlink" title="在命令提示窗口中删除数据表"></a>在命令提示窗口中删除数据表</h3><p>在 <code>mysql&gt;</code> 命令提示窗口中删除数据表 SQL 语句为 DROP TABLE：</p><p>以下实例删除了数据表 <code>tbl</code>:</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; DROP TABLE tbl</span><br><span class="line">Query OK, 0 rows affected (0.8 sec)</span><br></pre></td></tr></table></figure></p><h2 id="插入数据"><a href="#插入数据" class="headerlink" title="插入数据"></a>插入数据</h2><p>MySQL 表中使用 <code>INSERT INTO</code> 语句来插入数据。</p><p>INSERT INTO 命令</p><p>以下为向 MySQL 数据表插入数据通用的 <code>INSERT INTO</code> 的 SQL 语法：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> table_name ( field1, field2,...fieldN )</span><br><span class="line">                       <span class="keyword">VALUES</span></span><br><span class="line">                       ( value1, value2,...valueN );</span><br></pre></td></tr></table></figure></p><p>如果数据是字符型，必须使用单引号或者双引号，如：<code>&quot;value&quot;</code>。</p><h3 id="通过命令提示窗口插入数据"><a href="#通过命令提示窗口插入数据" class="headerlink" title="通过命令提示窗口插入数据"></a>通过命令提示窗口插入数据</h3><p>以下我们将使用 <code>INSERT INTO</code> 语句向 MySQL 数据表 <code>tbl</code> 插入数据</p><p>以下实例中我们将向 <code>tbl</code> 表插入三条数据:</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; INSERT INTO tbl </span><br><span class="line">    -&gt; (title, author, submission_date)</span><br><span class="line">    -&gt; VALUES</span><br><span class="line">    -&gt; (&quot;学习 PHP&quot;, &quot;PHP&quot;, NOW());</span><br><span class="line">Query OK, 1 rows affected, 1 warnings (0.01 sec)</span><br><span class="line">mysql&gt; INSERT INTO tbl</span><br><span class="line">    -&gt; (title, author, submission_date)</span><br><span class="line">    -&gt; VALUES</span><br><span class="line">    -&gt; (&quot;学习 MySQL&quot;, &quot;MySQL&quot;, NOW());</span><br><span class="line">Query OK, 1 rows affected, 1 warnings (0.01 sec)</span><br><span class="line">mysql&gt; INSERT INTO tbl</span><br><span class="line">    -&gt; (title, author, submission_date)</span><br><span class="line">    -&gt; VALUES</span><br><span class="line">    -&gt; (&quot;学习 JAVA&quot;, &quot;JAVA&quot;, &#x27;2021-01-01&#x27;);</span><br><span class="line">Query OK, 1 rows affected (0.00 sec)</span><br><span class="line">mysql&gt; INSERT INTO tbl</span><br><span class="line">    -&gt; (title, author, submission_date)</span><br><span class="line">    -&gt; VALUES</span><br><span class="line">    -&gt; (&quot;学习 Python&quot;, &quot;Python&quot;, &#x27;2021-01-01&#x27;);</span><br><span class="line">Query OK, 1 rows affected, 1 warnings (0.01 sec)</span><br></pre></td></tr></table></figure></p><p><strong>注意</strong>：使用箭头标记 <code>-&gt;</code> 不是 SQL 语句的一部分，它仅仅表示一个新行，如果一条 SQL 语句太长，我们可以通过回车键来创建一个新行来编写 SQL 语句，SQL 语句的命令结束符为分号 <code>;</code>。</p><p>在以上实例中，我们并没有提供 <code>id</code> 的数据，因为该字段我们在创建表的时候已经设置它为 <code>AUTO_INCREMENT</code>(自动增加) 属性。 所以，该字段会自动递增而不需要我们去设置。实例中 <code>NOW()</code> 是一个 MySQL 函数，该函数返回日期和时间。</p><h2 id="查询数据"><a href="#查询数据" class="headerlink" title="查询数据"></a>查询数据</h2><h3 id="SELECT-命令"><a href="#SELECT-命令" class="headerlink" title="SELECT 命令"></a>SELECT 命令</h3><p>MySQL 数据库使用 <code>SELECT</code> 语句来查询数据。</p><p>以下为在 MySQL 数据库中查询数据通用的 SELECT 语法：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> column_name, column_name</span><br><span class="line"><span class="keyword">FROM</span> table_name</span><br><span class="line">[<span class="keyword">WHERE</span> Clause]</span><br><span class="line">[<span class="keyword">LIMIT</span> N][ <span class="keyword">OFFSET</span> M]</span><br></pre></td></tr></table></figure></p><ul><li>查询语句中你可以使用一个或者多个表，表之间使用逗号 <code>,</code> 分割，并使用 <code>WHERE</code> 语句来设定查询条件。</li><li><code>SELECT</code> 命令可以读取一条或者多条记录。</li><li>你可以使用星号 <code>*</code> 来代替其他字段，<code>SELECT</code> 语句会返回表的所有字段数据。</li><li>你可以使用 <code>WHERE</code> 语句来包含任何条件。</li><li>你可以使用 <code>LIMIT</code> 属性来设定返回的记录数。</li><li>你可以通过 <code>OFFSET</code> 指定 <code>SELECT</code> 语句开始查询的数据偏移量。默认情况下偏移量为 0。</li></ul><h3 id="在命令提示窗口中获取数据"><a href="#在命令提示窗口中获取数据" class="headerlink" title="在命令提示窗口中获取数据"></a>在命令提示窗口中获取数据</h3><p>以下实例我们将通过 <code>SELECT</code> 命令来获取数据表 <code>tbl</code> 的数据：</p><p>读取数据表：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> tbl;</span><br></pre></td></tr></table></figure></p><p>输出结果：<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM tbl;</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">| id | title       | author | submission_date |</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">|  1 | 学习 PHP    | PHP    | 2021-01-12      |</span><br><span class="line">|  2 | 学习 MySQL  | MySQL  | 2021-01-12      |</span><br><span class="line">|  3 | 学习 JAVA   | JAVA   | 2021-01-01      |</span><br><span class="line">|  4 | 学习 Python | Python | 2021-01-01      |</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">4 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><h2 id="WHERE-子句"><a href="#WHERE-子句" class="headerlink" title="WHERE 子句"></a>WHERE 子句</h2><h3 id="WHERE-子句语法"><a href="#WHERE-子句语法" class="headerlink" title="WHERE 子句语法"></a>WHERE 子句语法</h3><p>我们知道从 MySQL 表中使用 <code>SELECT</code> 语句来读取数据。</p><p>如需有条件地从表中选取数据，可将 <code>WHERE</code> 子句添加到 <code>SELECT</code> 语句中。</p><p>语法</p><p>以下是 <code>SELECT</code> 语句使用 <code>WHERE</code> 子句从数据表中读取数据的通用语法：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> field1, field2,...fieldN <span class="keyword">FROM</span> table_name1, table_name2...</span><br><span class="line">[<span class="keyword">WHERE</span> condition1 [<span class="keyword">AND</span> [<span class="keyword">OR</span>]] condition2.....</span><br></pre></td></tr></table></figure></p><ul><li>查询语句中你可以使用一个或者多个表，表之间使用逗号, 分割，并使用 <code>WHERE</code> 语句来设定查询条件。</li><li>你可以在 <code>WHERE</code> 子句中指定任何条件。</li><li>你可以使用 <code>AND</code> 或者 <code>OR</code> 指定一个或多个条件。</li><li><code>WHERE</code> 子句也可以运用于 SQL 的 <code>DELETE</code> 或者 <code>UPDATE</code> 命令。</li><li><code>WHERE</code> 子句类似于程序语言中的 <code>if</code> 条件，根据 MySQL 表中的字段值来读取指定的数据。</li></ul><p>以下为操作符列表，可用于 <code>WHERE</code> 子句中。</p><p>下表中实例假定 A 为 10, B 为 20</p><div class="table-container"><table><thead><tr><th style="text-align:center">操作符</th><th style="text-align:center">描述</th><th style="text-align:center">实例</th></tr></thead><tbody><tr><td style="text-align:center">=</td><td style="text-align:center">等号，检测两个值是否相等，<br>如果相等返回 true</td><td style="text-align:center">(A = B) 返回 false。</td></tr><tr><td style="text-align:center">&lt;&gt;, !=</td><td style="text-align:center">不等于，检测两个值是否相等，<br>如果不相等返回 true</td><td style="text-align:center">(A != B) 返回 true。</td></tr><tr><td style="text-align:center">&gt;</td><td style="text-align:center">大于号，检测左边的值是否大于右边的值, <br>如果左边的值大于右边的值返回 true</td><td style="text-align:center">(A &gt; B) 返回 false。</td></tr><tr><td style="text-align:center">&lt;</td><td style="text-align:center">小于号，检测左边的值是否小于右边的值, <br>如果左边的值小于右边的值返回 true</td><td style="text-align:center">(A &lt; B) 返回 true。</td></tr><tr><td style="text-align:center">&gt;=</td><td style="text-align:center">大于等于号，检测左边的值是否大于或等于右边的值, <br>如果左边的值大于或等于右边的值返回 true</td><td style="text-align:center">(A &gt;= B) 返回 false。</td></tr><tr><td style="text-align:center">&lt;=</td><td style="text-align:center">小于等于号，检测左边的值是否小于或等于右边的值, <br>如果左边的值小于或等于右边的值返回 true</td><td style="text-align:center">(A &lt;= B) 返回 true。</td></tr></tbody></table></div><p>如果我们想在 MySQL 数据表中读取指定的数据，<code>WHERE</code> 子句是非常有用的。</p><p>使用主键来作为 <code>WHERE</code> 子句的条件查询是非常快速的。</p><p>如果给定的条件在表中没有任何匹配的记录，那么查询不会返回任何数据。</p><h3 id="BINARY-关键字"><a href="#BINARY-关键字" class="headerlink" title="BINARY 关键字"></a>BINARY 关键字</h3><p>MySQL 的 <code>WHERE</code> 子句的字符串比较是不区分大小写的。 你可以使用 <code>BINARY</code> 关键字来设定 <code>WHERE</code> 子句的字符串比较是区分大小写的。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM tbl WHERE BINARY author=&#x27;mysql&#x27;;</span><br><span class="line">Empty <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> tbl <span class="keyword">WHERE</span> <span class="built_in">BINARY</span> author=<span class="string">&#x27;MySQL&#x27;</span>;</span><br><span class="line">+<span class="comment">----+------------+--------+-----------------+</span></span><br><span class="line">| id | title      | author | submission_date |</span><br><span class="line">+<span class="comment">----+------------+--------+-----------------+</span></span><br><span class="line">|  2 | 学习 MySQL | MySQL  | 2021-01-12      |</span><br><span class="line">+<span class="comment">----+------------+--------+-----------------+</span></span><br></pre></td></tr></table></figure></p><p>实例中使用了 <code>BINARY</code> 关键字，是区分大小写的，所以 <code>author=&#39;mysql&#39;</code> 的查询条件是没有数据的。</p><h2 id="UPDATE-更新"><a href="#UPDATE-更新" class="headerlink" title="UPDATE 更新"></a>UPDATE 更新</h2><p>如果我们需要修改或更新 MySQL 中的数据，我们可以使用 <code>UPDATE</code> 命令来操作。</p><p>以下是 <code>UPDATE</code> 命令修改 MySQL 数据表数据的通用 SQL 语法：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">UPDATE</span> table_name <span class="keyword">SET</span> field1=<span class="keyword">new</span>-value1, field2=<span class="keyword">new</span>-value2</span><br><span class="line">[<span class="keyword">WHERE</span> Clause]</span><br></pre></td></tr></table></figure></p><ul><li>你可以同时更新一个或多个字段。</li><li>你可以在 <code>WHERE</code> 子句中指定任何条件。</li><li>你可以在一个单独表中同时更新数据。</li><li>当你需要更新数据表中指定行的数据时 <code>WHERE</code> 子句是非常有用的。</li></ul><p>在命令提示窗口中更新数据</p><p>以下我们将在 <code>UPDATE</code> 命令使用 <code>WHERE</code> 子句来更新 <code>tbl</code> 表中指定的数据：</p><p>以下实例将更新数据表中 <code>id</code> 为 <code>3</code> 的 <code>title</code> 字段值：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; UPDATE tbl SET title=&#x27;学习 C++&#x27; WHERE id = 3;</span><br><span class="line">Query OK, 1 rows affected (0.01 sec)</span><br><span class="line"> </span><br><span class="line">mysql&gt; SELECT * FROM tbl WHERE id = 3;</span><br><span class="line">+<span class="comment">----+----------+--------+-----------------+</span></span><br><span class="line">| id | title    | author | submission_date |</span><br><span class="line">+<span class="comment">----+----------+--------+-----------------+</span></span><br><span class="line">|  3 | 学习 C++ | JAVA   | 2021-01-01      |</span><br><span class="line">+<span class="comment">----+----------+--------+-----------------+</span></span><br></pre></td></tr></table></figure></p><p>从结果上看，<code>id</code> 为 <code>3</code> 的 <code>title</code> 已被修改。</p><h2 id="DELETE-语句"><a href="#DELETE-语句" class="headerlink" title="DELETE 语句"></a>DELETE 语句</h2><h3 id="DELETE-FROM-命令"><a href="#DELETE-FROM-命令" class="headerlink" title="DELETE FROM 命令"></a>DELETE FROM 命令</h3><p>你可以使用 SQL 的 <code>DELETE FROM</code> 命令来删除 MySQL 数据表中的记录。</p><p>以下是 SQL DELETE 语句从 MySQL 数据表中删除数据的通用语法：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> table_name [<span class="keyword">WHERE</span> Clause]</span><br></pre></td></tr></table></figure></p><ul><li>如果没有指定 <code>WHERE</code> 子句，MySQL 表中的所有记录将被删除。</li><li>你可以在 <code>WHERE</code> 子句中指定任何条件</li><li>您可以在单个表中一次性删除记录。</li></ul><p>当你想删除数据表中指定的记录时 <code>WHERE</code> 子句是非常有用的。</p><h3 id="在命令提示窗口中删除数据"><a href="#在命令提示窗口中删除数据" class="headerlink" title="在命令提示窗口中删除数据"></a>在命令提示窗口中删除数据</h3><p>这里我们将在 <code>DELETE</code> 命令中使用 <code>WHERE</code> 子句来删除 MySQL 数据表 tbl 所选的数据。</p><p>以下实例将删除 tbl 表中 id 为 3 的记录：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; DELETE FROM tbl WHERE id=3;</span><br><span class="line">Query OK, 1 row affected (0.23 sec)</span><br></pre></td></tr></table></figure></p><h3 id="多表连接删除"><a href="#多表连接删除" class="headerlink" title="多表连接删除"></a>多表连接删除</h3><p>在 MySQL 中可以同时删除两个表以上的数据：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DELETE</span> orders, items <span class="keyword">FROM</span> orders, items </span><br><span class="line"><span class="keyword">WHERE</span> orders.userid = items.userid </span><br><span class="line"><span class="keyword">AND</span> orders.orderid = items.orderid </span><br></pre></td></tr></table></figure></p><h2 id="LIKE-子句"><a href="#LIKE-子句" class="headerlink" title="LIKE 子句"></a>LIKE 子句</h2><p>我们知道在 MySQL 中使用 <code>SELECT</code> 命令来读取数据， 同时我们可以在 <code>SELECT</code> 语句中使用 <code>WHERE</code> 子句来获取指定的记录。</p><p><code>WHERE</code> 子句中可以使用等号 <code>=</code> 来设定获取数据的条件，如 <code>author = &#39;MySQL&#39;</code>。</p><p>但是有时候我们需要获取 <code>author</code> 字段含有 <code>P</code> 字符的所有记录，这时我们就需要在 <code>WHERE</code> 子句中使用 <code>LIKE</code> 子句。</p><p><code>LIKE</code> 子句中使用百分号 <code>%</code> 字符来表示任意字符，类似于 <code>UNIX</code> 或正则表达式中的星号 <code>*</code>。</p><p>如果没有使用百分号 <code>%</code>, <code>LIKE</code> 子句与等号 = 的效果是一样的。</p><h3 id="LIKE-子句语法"><a href="#LIKE-子句语法" class="headerlink" title="LIKE 子句语法"></a>LIKE 子句语法</h3><p>以下是 SELECT 语句使用 <code>LIKE</code> 子句从数据表中读取数据的通用语法：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> field1, field2,...fieldN </span><br><span class="line"><span class="keyword">FROM</span> table_name</span><br><span class="line"><span class="keyword">WHERE</span> field1 <span class="keyword">LIKE</span> condition1 [<span class="keyword">AND</span> [<span class="keyword">OR</span>]] filed2 = <span class="string">&#x27;somevalue&#x27;</span></span><br></pre></td></tr></table></figure></p><ul><li>你可以在 <code>WHERE</code> 子句中指定任何条件。</li><li>你可以在 <code>WHERE</code> 子句中使用 <code>LIKE</code> 子句。</li><li>你可以使用 <code>LIKE</code> 子句代替等号 <code>=</code>。</li><li><code>LIKE</code> 通常与 <code>%</code> 一同使用，类似于一个元字符的搜索。</li><li>你可以使用 <code>AND</code> 或者 <code>OR</code> 指定一个或多个条件。</li><li>你可以在 <code>DELETE</code> 或 <code>UPDATE</code> 命令中使用 <code>WHERE...LIKE</code> 子句来指定条件。</li></ul><h3 id="在命令提示符中使用-LIKE-子句"><a href="#在命令提示符中使用-LIKE-子句" class="headerlink" title="在命令提示符中使用 LIKE 子句"></a>在命令提示符中使用 LIKE 子句</h3><p>以下我们将在 <code>SELECT</code> 命令中使用 <code>WHERE...LIKE</code> 子句来从 MySQL 数据表 <code>tbl</code> 中读取数据。</p><p>以下是我们将 <code>tbl</code> 表中获取 <code>author</code> 字段中以 P 为开头的所有记录：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM tbl  WHERE author LIKE &#x27;P%&#x27;;</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">| id | title       | author | submission_date |</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">|  1 | 学习 PHP    | PHP    | 2021-01-12      |</span><br><span class="line">|  4 | 学习 Python | Python | 2021-01-01      |</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><h2 id="UNION-操作符"><a href="#UNION-操作符" class="headerlink" title="UNION 操作符"></a>UNION 操作符</h2><p>下面为大家介绍 MySQL <code>UNION</code> 操作符的语法和实例。</p><p>MySQL <code>UNION</code> 操作符用于连接两个以上的 <code>SELECT</code> 语句的结果组合到一个结果集合中。多个 <code>SELECT</code> 语句会删除重复的数据。</p><h3 id="UNION-操作符语法"><a href="#UNION-操作符语法" class="headerlink" title="UNION 操作符语法"></a>UNION 操作符语法</h3><p>MySQL <code>UNION</code> 操作符语法格式：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> expression1, expression2, ... expression_n</span><br><span class="line"><span class="keyword">FROM</span> <span class="keyword">tables</span></span><br><span class="line">[<span class="keyword">WHERE</span> conditions]</span><br><span class="line"><span class="keyword">UNION</span> [<span class="keyword">ALL</span> | <span class="keyword">DISTINCT</span>]</span><br><span class="line"><span class="keyword">SELECT</span> expression1, expression2, ... expression_n</span><br><span class="line"><span class="keyword">FROM</span> <span class="keyword">tables</span></span><br><span class="line">[<span class="keyword">WHERE</span> conditions];</span><br></pre></td></tr></table></figure></p><p>参数</p><ul><li><strong>expression1, expression2, ... expression_n</strong>: 要检索的列。</li><li><strong>tables</strong>: 要检索的数据表。</li><li><strong>WHERE conditions</strong>: 可选，检索条件。</li><li><strong>DISTINCT</strong>: 可选，删除结果集中重复的数据。默认情况下 <code>UNION</code> 操作符已经删除了重复数据，所以 <code>DISTINCT</code> 修饰符对结果没啥影响。</li><li><strong>ALL</strong>: 可选，返回所有结果集，包含重复数据。</li></ul><p>演示数据库</p><p>下面是选自 <code>Websites</code> 表的数据：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM Websites;</span><br><span class="line">+<span class="comment">----+--------------+---------------------------+-------+---------+</span></span><br><span class="line">| id | name         | url                       | alexa | country |</span><br><span class="line">+<span class="comment">----+--------------+---------------------------+-------+---------+</span></span><br><span class="line">| 1  | Google       | https://www.google.cm/    | 1     | USA     |</span><br><span class="line">| 2  | 淘宝          | https://www.taobao.com/   | 13    | CN      |</span><br><span class="line">| 3  | 微博          | https://weibo.com/         | 20    | CN      |</span><br><span class="line">| 4  | Facebook     | https://www.facebook.com/ | 3     | USA     |</span><br><span class="line">| 5  | stackoverflow | https://stackoverflow.com/ |   0 | IND     |</span><br><span class="line">+<span class="comment">----+---------------+---------------------------+-------+---------+</span></span><br></pre></td></tr></table></figure></p><p>下面是 <code>apps</code> 表的数据：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM apps;</span><br><span class="line">+<span class="comment">----+------------+-------------------------+---------+</span></span><br><span class="line">| id | app_name   | url                     | country |</span><br><span class="line">+<span class="comment">----+------------+-------------------------+---------+</span></span><br><span class="line">|  1 | QQ APP     | https://im.qq.com/       | CN      |</span><br><span class="line">|  2 | 微博 APP | https://weibo.com/       | CN      |</span><br><span class="line">|  3 | 淘宝 APP | https://www.taobao.com/ | CN      |</span><br><span class="line">+<span class="comment">----+------------+-------------------------+---------+</span></span><br><span class="line">3 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><h3 id="在命令提示符中使用-UNION-操作符"><a href="#在命令提示符中使用-UNION-操作符" class="headerlink" title="在命令提示符中使用 UNION 操作符"></a>在命令提示符中使用 UNION 操作符</h3><p>下面的 SQL 语句从 <code>Websites</code> 和 <code>apps</code> 表中选取所有不同的 <code>country</code>（只有不同的值），执行 SQL 输出结果如下：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT country FROM Websites</span><br><span class="line">    -&gt; UNION</span><br><span class="line">    -&gt; SELECT country FROM apps</span><br><span class="line">    -&gt; ORDER BY country;</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">| country |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">|  CN     |</span><br><span class="line">|  IND    |</span><br><span class="line">|  USA    |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">3 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p><strong>注释</strong>：<code>UNION</code> 不能用于列出两个表中所有的 <code>country</code>。如果一些网站和 APP 来自同一个国家，每个国家只会列出一次。<code>UNION</code> 只会选取不同的值。请使用 <code>UNION ALL</code> 来选取重复的值。</p><h3 id="在命令提示符中使用-UNION-ALL-操作符"><a href="#在命令提示符中使用-UNION-ALL-操作符" class="headerlink" title="在命令提示符中使用 UNION ALL 操作符"></a>在命令提示符中使用 UNION ALL 操作符</h3><p>下面的 SQL 语句使用 <code>UNION ALL</code> 从 <code>Websites</code> 和 <code>apps</code> 表中选取所有的 <code>country</code>（有重复的值）：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT country FROM Websites</span><br><span class="line">    -&gt; UNION ALL</span><br><span class="line">    -&gt; SELECT country FROM apps</span><br><span class="line">    -&gt; ORDER BY country;</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">| country |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">|  CN     |</span><br><span class="line">|  CN     |</span><br><span class="line">|  CN     |</span><br><span class="line">|  CN     |</span><br><span class="line">|  IND    |</span><br><span class="line">|  USA    |</span><br><span class="line">|  USA    |</span><br><span class="line">|  USA    |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">8 rows in <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br></pre></td></tr></table></figure></p><p><strong>带有 <code>WHERE</code> 的 <code>UNION ALL</code></strong></p><p>下面的 SQL 语句使用 <code>UNION ALL</code> 从 <code>Websites</code> 和 <code>apps</code> 表中选取所有的中国(CN)的数据（有重复的值）：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT country, name FROM Websites</span><br><span class="line">    -&gt; WHERE country=&#x27;CN&#x27;</span><br><span class="line">    -&gt; UNION ALL</span><br><span class="line">    -&gt; SELECT country, app_name FROM apps</span><br><span class="line">    -&gt; WHERE country=&#x27;CN&#x27;</span><br><span class="line">    -&gt; ORDER BY country;</span><br><span class="line">+<span class="comment">---------+------------+</span></span><br><span class="line">| country | name       |</span><br><span class="line">+<span class="comment">---------+------------+</span></span><br><span class="line">| CN      | 淘宝       |</span><br><span class="line">| CN      | QQ APP     |</span><br><span class="line">| CN      | 微博 APP   |</span><br><span class="line">| CN      | 微博       |</span><br><span class="line">| CN      | 淘宝 APP   |</span><br><span class="line">+<span class="comment">---------+------------+</span></span><br><span class="line">5 rows in <span class="keyword">set</span> (<span class="number">0.03</span> sec)</span><br></pre></td></tr></table></figure></p><h2 id="排序语句"><a href="#排序语句" class="headerlink" title="排序语句"></a>排序语句</h2><p>我们知道从 MySQL 表中使用 <code>SELECT</code> 语句来读取数据。</p><p>如果我们需要对读取的数据进行排序，我们就可以使用 MySQL 的 <code>ORDER BY</code> 子句来设定你想按哪个字段哪种方式来进行排序，再返回搜索结果。</p><h3 id="ORDER-BY-子句语法"><a href="#ORDER-BY-子句语法" class="headerlink" title="ORDER BY 子句语法"></a>ORDER BY 子句语法</h3><p>以下是 <code>SELECT</code> 语句使用 <code>ORDER BY</code> 子句将查询数据排序后再返回数据：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> field1, field2,...fieldN <span class="keyword">FROM</span> table_name1, table_name2...</span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> field1 [<span class="keyword">ASC</span> [<span class="keyword">DESC</span>][默认 <span class="keyword">ASC</span>]], [field2...] [<span class="keyword">ASC</span> [<span class="keyword">DESC</span>][默认 <span class="keyword">ASC</span>]]</span><br></pre></td></tr></table></figure></p><ul><li>你可以使用任何字段来作为排序的条件，从而返回排序后的查询结果。</li><li>你可以设定多个字段来排序。</li><li>你可以使用 <code>ASC</code> 或 <code>DESC</code> 关键字来设置查询结果是按升序或降序排列。默认情况下，它是按升序排列。</li><li>你可以添加 <code>WHERE...LIKE</code> 子句来设置条件。</li></ul><h3 id="在命令提示符中使用-ORDER-BY-子句"><a href="#在命令提示符中使用-ORDER-BY-子句" class="headerlink" title="在命令提示符中使用 ORDER BY 子句"></a>在命令提示符中使用 ORDER BY 子句</h3><p>以下将在 <code>SELECT</code> 语句中使用 <code>ORDER BY</code> 子句来读取 MySQL 数据表 <code>tbl</code> 中的数据：</p><p>尝试以下实例，结果将按升序及降序排列。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM tbl ORDER BY submission_date ASC;</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">| id | title       | author | submission_date |</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">|  3 | 学习 C++    | JAVA   | 2021-01-01      |</span><br><span class="line">|  4 | 学习 Python | Python | 2021-01-01      |</span><br><span class="line">|  1 | 学习 PHP    | PHP    | 2021-01-12      |</span><br><span class="line">|  2 | 学习 MySQL  | MySQL  | 2021-01-12      |</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">4 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> tbl <span class="keyword">ORDER</span> <span class="keyword">BY</span> submission_date <span class="keyword">DESC</span>;</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">| id | title       | author | submission_date |</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">|  1 | 学习 PHP    | PHP    | 2021-01-12      |</span><br><span class="line">|  2 | 学习 MySQL  | MySQL  | 2021-01-12      |</span><br><span class="line">|  3 | 学习 C++    | JAVA   | 2021-01-01      |</span><br><span class="line">|  4 | 学习 Python | Python | 2021-01-01      |</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">4 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><h2 id="分组语句"><a href="#分组语句" class="headerlink" title="分组语句"></a>分组语句</h2><p>GROUP BY 语句根据一个或多个列对结果集进行分组。</p><h3 id="GROUP-BY-子句语法"><a href="#GROUP-BY-子句语法" class="headerlink" title="GROUP BY 子句语法"></a>GROUP BY 子句语法</h3><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> column_name, <span class="keyword">function</span>(column_name)</span><br><span class="line"><span class="keyword">FROM</span> table_name</span><br><span class="line"><span class="keyword">WHERE</span> column_name <span class="keyword">operator</span> <span class="keyword">value</span></span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> column_name;</span><br><span class="line"></span><br></pre></td></tr></table></figure></p><p>本章节实例使用到了以下表结构及数据，使用前我们可以先将以下数据导入数据库中。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> <span class="keyword">IF</span> <span class="keyword">EXISTS</span> <span class="string">`employee_tbl`</span>;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="string">`employee_tbl`</span> (</span><br><span class="line">  <span class="string">`id`</span> <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`name`</span> <span class="built_in">char</span>(<span class="number">10</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">  <span class="string">`date`</span> datetime <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">  <span class="string">`singin`</span> <span class="built_in">tinyint</span>(<span class="number">4</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> <span class="keyword">COMMENT</span> <span class="string">&#x27;登录次数&#x27;</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span> (<span class="string">`id`</span>)</span><br><span class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span>=utf8;</span><br><span class="line"></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> <span class="string">`employee_tbl`</span> <span class="keyword">VALUES</span> </span><br><span class="line">(<span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;小明&#x27;</span>, <span class="string">&#x27;2016-04-22 15:25:33&#x27;</span>, <span class="string">&#x27;1&#x27;</span>), </span><br><span class="line">(<span class="string">&#x27;2&#x27;</span>, <span class="string">&#x27;小王&#x27;</span>, <span class="string">&#x27;2016-04-20 15:25:47&#x27;</span>, <span class="string">&#x27;3&#x27;</span>), </span><br><span class="line">(<span class="string">&#x27;3&#x27;</span>, <span class="string">&#x27;小丽&#x27;</span>, <span class="string">&#x27;2016-04-19 15:26:02&#x27;</span>, <span class="string">&#x27;2&#x27;</span>), </span><br><span class="line">(<span class="string">&#x27;4&#x27;</span>, <span class="string">&#x27;小王&#x27;</span>, <span class="string">&#x27;2016-04-07 15:26:14&#x27;</span>, <span class="string">&#x27;4&#x27;</span>), </span><br><span class="line">(<span class="string">&#x27;5&#x27;</span>, <span class="string">&#x27;小明&#x27;</span>, <span class="string">&#x27;2016-04-11 15:26:40&#x27;</span>, <span class="string">&#x27;4&#x27;</span>), </span><br><span class="line">(<span class="string">&#x27;6&#x27;</span>, <span class="string">&#x27;小明&#x27;</span>, <span class="string">&#x27;2016-04-04 15:26:54&#x27;</span>, <span class="string">&#x27;2&#x27;</span>);</span><br></pre></td></tr></table></figure></p><p>导入成功后，执行以下 SQL 语句：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM employee_tbl;</span><br><span class="line">+<span class="comment">----+------+---------------------+--------+</span></span><br><span class="line">| id | name | date                | singin |</span><br><span class="line">+<span class="comment">----+------+---------------------+--------+</span></span><br><span class="line">|  1 | 小明 | 2016-04-22 15:25:33 |      1 |</span><br><span class="line">|  2 | 小王 | 2016-04-20 15:25:47 |      3 |</span><br><span class="line">|  3 | 小丽 | 2016-04-19 15:26:02 |      2 |</span><br><span class="line">|  4 | 小王 | 2016-04-07 15:26:14 |      4 |</span><br><span class="line">|  5 | 小明 | 2016-04-11 15:26:40 |      4 |</span><br><span class="line">|  6 | 小明 | 2016-04-04 15:26:54 |      2 |</span><br><span class="line">+<span class="comment">----+------+---------------------+--------+</span></span><br><span class="line">6 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p>接下来我们使用 <code>GROUP BY</code> 语句 将数据表按名字进行分组，并统计每个人有多少条记录：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT name, COUNT(*) FROM employee_tbl GROUP BY name;</span><br><span class="line">+<span class="comment">------+----------+</span></span><br><span class="line">| name | COUNT(*) |</span><br><span class="line">+<span class="comment">------+----------+</span></span><br><span class="line">| 小丽 |        1 |</span><br><span class="line">| 小明 |        3 |</span><br><span class="line">| 小王 |        2 |</span><br><span class="line">+<span class="comment">------+----------+</span></span><br><span class="line">3 rows in <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br></pre></td></tr></table></figure></p><h3 id="使用-WITH-ROLLUP"><a href="#使用-WITH-ROLLUP" class="headerlink" title="使用 WITH ROLLUP"></a>使用 WITH ROLLUP</h3><p><code>WITH ROLLUP</code> 可以实现在分组统计数据基础上再进行相同的统计（SUM, AVG, COUNT…）。</p><p>例如我们将以上的数据表按名字进行分组，再统计每个人登录的次数：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT name, SUM(singin) AS singin_count FROM employee_tbl GROUP BY name WITH ROLLUP;</span><br><span class="line">+<span class="comment">------+--------------+</span></span><br><span class="line">| name | singin_count |</span><br><span class="line">+<span class="comment">------+--------------+</span></span><br><span class="line">| 小丽 |            2 |</span><br><span class="line">| 小明 |            7 |</span><br><span class="line">| 小王 |            7 |</span><br><span class="line">| NULL |           16 |</span><br><span class="line">+<span class="comment">------+--------------+</span></span><br><span class="line">4 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p>其中记录 <code>NULL</code> 表示所有人的登录次数。</p><p>我们可以使用 <code>COALESCE</code> 来设置一个可以取代 <code>NUll</code> 的名称</p><p> <strong> COALESCE 语法：</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">COALESCE</span>(a, b, c) <span class="keyword">FROM</span> ...;</span><br></pre></td></tr></table></figure></p><p>参数说明：如果 <code>a == NULL</code>，则选择 <code>b</code>；如果<code>b == NULL</code>，则选择 <code>c</code>；如果 <code>a != NULL</code>，则选择<code>a</code>；如果 <code>a</code> <code>b</code> <code>c</code> 都为 <code>NULL</code> ，则返回为 <code>NULL</code>（没意义）。</p><p>以下实例中如果名字为空我们使用总数代替：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT COALESCE(name, &#x27;总数&#x27;), SUM(singin) AS singin_count FROM employee_tbl GROUP BY name WITH ROLLUP;</span><br><span class="line">+<span class="comment">------------------------+--------------+</span></span><br><span class="line">| COALESCE(name, &#x27;总数&#x27;) | singin_count |</span><br><span class="line">+<span class="comment">------------------------+--------------+</span></span><br><span class="line">| 小丽                   |            2 |</span><br><span class="line">| 小明                   |            7 |</span><br><span class="line">| 小王                   |            7 |</span><br><span class="line">| 总数                   |           16 |</span><br><span class="line">+<span class="comment">------------------------+--------------+</span></span><br><span class="line">4 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><h2 id="JOIN"><a href="#JOIN" class="headerlink" title="JOIN"></a>JOIN</h2><p>在前几章节中，我们已经学会了如何在一张表中读取数据，这是相对简单的，但是在真正的应用中经常需要从多个数据表中读取数据。</p><p>本章节将向大家介绍如何使用 MySQL 的 <code>JOIN</code> 在两个或多个表中查询数据。</p><p>你可以在 <code>SELECT</code>、<code>UPDATE</code> 和 <code>DELETE</code> 语句中使用 MySQL 的 <code>JOIN</code> 来联合多表查询。</p><p>JOIN 按照功能大致分为如下三类：</p><ul><li><strong>INNER JOIN（内连接或等值连接）</strong>：获取两个表中字段匹配关系的记录。</li><li><strong>LEFT JOIN（左连接）</strong>：获取左表所有记录，即使右表没有对应匹配的记录。</li><li><strong>RIGHT JOIN（右连接）</strong>： 与 <code>LEFT JOIN</code> 相反，用于获取右表所有记录，即使左表没有对应匹配的记录。</li></ul><h3 id="INNER-JOIN"><a href="#INNER-JOIN" class="headerlink" title="INNER JOIN"></a>INNER JOIN</h3><p>我们在 <code>testDB</code> 数据库中有两张表 <code>tcount_tbl</code> 和 <code>tbl</code>。两张数据表数据如下：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM tcount_tbl;</span><br><span class="line">+<span class="comment">--------+-------+</span></span><br><span class="line">| author | count |</span><br><span class="line">+<span class="comment">--------+-------+</span></span><br><span class="line">| JAVA   |    10 |</span><br><span class="line">| MySQL  |    20 |</span><br><span class="line">| C++    |    30 |</span><br><span class="line">+<span class="comment">--------+-------+</span></span><br><span class="line">3 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br><span class="line"> </span><br><span class="line">mysql&gt; <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> tbl;</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">| id | title       | author | submission_date |</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">|  1 | 学习 PHP    | PHP    | 2021-01-12      |</span><br><span class="line">|  2 | 学习 MySQL  | MySQL  | 2021-01-12      |</span><br><span class="line">|  3 | 学习 C++    | JAVA   | 2021-01-01      |</span><br><span class="line">|  4 | 学习 Python | Python | 2021-01-01      |</span><br><span class="line">|  5 | MySQL 教程  | MySQL  | 2021-01-12      |</span><br><span class="line">|  6 | JAVA 教程   | JAVA   | 2021-01-12      |</span><br><span class="line">+<span class="comment">----+-------------+--------+-----------------+</span></span><br><span class="line">6 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p>接下来我们就使用 <code>MySQL</code> 的<code>INNER JOIN</code> (也可以省略 <code>INNER</code> 使用 <code>JOIN</code>，效果一样)来连接以上两张表来读取 <code>tbl</code> 表中所有 <code>author</code> 字段在 <code>tcount_tbl</code> 表对应的 <code>count</code> 字段值：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt;  SELECT a.id, a.author, b.count FROM tbl a INNER JOIN tcount_tbl b ON a.author = b.author;</span><br><span class="line">+<span class="comment">----+--------+-------+</span></span><br><span class="line">| id | author | count |</span><br><span class="line">+<span class="comment">----+--------+-------+</span></span><br><span class="line">|  2 | MySQL  |    20 |</span><br><span class="line">|  3 | JAVA   |    10 |</span><br><span class="line">|  5 | MySQL  |    20 |</span><br><span class="line">|  6 | JAVA   |    10 |</span><br><span class="line">+<span class="comment">----+--------+-------+</span></span><br><span class="line">4 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p>以上 SQL 语句等价于以下 <code>WHERE</code> 子句：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT a.id, a.author, b.count FROM tbl a, tcount_tbl b WHERE a.author = b.author;</span><br><span class="line">+<span class="comment">----+--------+-------+</span></span><br><span class="line">| id | author | count |</span><br><span class="line">+<span class="comment">----+--------+-------+</span></span><br><span class="line">|  2 | MySQL  |    20 |</span><br><span class="line">|  3 | JAVA   |    10 |</span><br><span class="line">|  5 | MySQL  |    20 |</span><br><span class="line">|  6 | JAVA   |    10 |</span><br><span class="line">+<span class="comment">----+--------+-------+</span></span><br><span class="line">4 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><h3 id="LEFT-JOIN"><a href="#LEFT-JOIN" class="headerlink" title="LEFT JOIN"></a>LEFT JOIN</h3><p>MySQL <code>LEFT JOIN</code> 与 <code>JOIN</code> 有所不同。 <code>LEFT JOIN</code> 会读取左边数据表的全部数据，即便右边表无对应数据。</p><p>尝试以下实例，以 <code>tbl</code> 为左表，<code>tcount_tbl</code> 为右表，理解 <code>LEFT JOIN</code> 的应用：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT a.id, a.author, b.count FROM tbl a LEFT JOIN tcount_tbl b ON a.author = b.author;</span><br><span class="line">+<span class="comment">----+--------+-------+</span></span><br><span class="line">| id | author | count |</span><br><span class="line">+<span class="comment">----+--------+-------+</span></span><br><span class="line">|  3 | JAVA   |    10 |</span><br><span class="line">|  6 | JAVA   |    10 |</span><br><span class="line">|  2 | MySQL  |    20 |</span><br><span class="line">|  5 | MySQL  |    20 |</span><br><span class="line">|  1 | PHP    |  NULL |</span><br><span class="line">|  4 | Python |  NULL |</span><br><span class="line">+<span class="comment">----+--------+-------+</span></span><br><span class="line">6 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p>以上实例中使用了 <code>LEFT JOIN</code>，该语句会读取左边的数据表 <code>tbl</code> 的所有选取的字段数据，即便在右侧表 <code>tcount_tbl</code> 中 没有对应的 <code>author</code> 字段值。</p><h3 id="RIGHT-JOIN"><a href="#RIGHT-JOIN" class="headerlink" title="RIGHT JOIN"></a>RIGHT JOIN</h3><p>MySQL <code>RIGHT JOIN</code> 会读取右边数据表的全部数据，即便左边边表无对应数据。</p><p>尝试以下实例，以 <code>tbl</code> 为左表，<code>tcount_tbl</code> 为右表，理解 <code>RIGHT JOIN</code> 的应用：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT a.id, a.author, b.count FROM tbl a RIGHT JOIN tcount_tbl b ON a.author = b.author;</span><br><span class="line">+<span class="comment">------+--------+-------+</span></span><br><span class="line">| id   | author | count |</span><br><span class="line">+<span class="comment">------+--------+-------+</span></span><br><span class="line">|    2 | MySQL  |    20 |</span><br><span class="line">|    3 | JAVA   |    10 |</span><br><span class="line">|    5 | MySQL  |    20 |</span><br><span class="line">|    6 | JAVA   |    10 |</span><br><span class="line">| NULL | NULL   |    30 |</span><br><span class="line">+<span class="comment">------+--------+-------+</span></span><br><span class="line">5 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p>以上实例中使用了 <code>RIGHT JOIN</code>，该语句会读取右边的数据表 <code>tcount_tbl</code> 的所有选取的字段数据，即便在左侧表 <code>tbl</code> 中没有对应的 <code>author</code> 字段值。</p><h2 id="NULL-值处理"><a href="#NULL-值处理" class="headerlink" title="NULL 值处理"></a>NULL 值处理</h2><p>我们已经知道 MySQL 使用 <code>SELECT</code> 命令及 <code>WHERE</code> 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时，该命令可能就无法正常工作。</p><h3 id="NULL-值运算符"><a href="#NULL-值运算符" class="headerlink" title="NULL 值运算符"></a>NULL 值运算符</h3><p>为了处理这种情况，MySQL 提供了三大运算符:</p><ul><li><strong>IS NULL</strong>: 当列的值是 <code>NULL</code>，此运算符返回 <code>true</code>。</li><li><strong>IS NOT NULL</strong>: 当列的值不为 <code>NULL</code>, 运算符返回 <code>true</code>。</li><li><strong>&lt;=&gt;</strong>: 比较操作符（不同于 <code>=</code> 运算符），当比较的两个值相等或者都为 <code>NULL</code> 时返回 <code>true</code>。</li></ul><p>关于 <code>NULL</code> 的条件比较运算是比较特殊的。你不能使用 <code>= NULL</code> 或 <code>!= NULL</code> 在列中查找 <code>NULL</code> 值 。</p><p>在 MySQL 中，<code>NULL</code> 值与任何其它值的比较（即使是 <code>NULL</code>）永远返回 <code>NULL</code>，即 <code>NULL = NULL</code> 返回 <code>NULL</code>。</p><p>MySQL 中处理 <code>NULL</code> 使用 <code>IS NULL</code> 和 <code>IS NOT NULL</code> 运算符。</p><p><strong>注意：</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> *, columnName1 + <span class="keyword">IFNULL</span>(columnName2, <span class="number">0</span>) <span class="keyword">FROM</span> tableName;</span><br></pre></td></tr></table></figure></p><p><code>columnName1</code>，<code>columnName2</code> 为 <code>int</code> 型，当 <code>columnName2</code> 中，有值为 <code>NULL</code> 时，<code>columnName1 + columnName2 = NULL</code>，<code>IFNULL(columnName2, 0)</code> 把 <code>columnName2</code> 中 <code>NULL</code> 值转为 <code>0</code>。</p><h3 id="在命令提示符中使用-NULL-值"><a href="#在命令提示符中使用-NULL-值" class="headerlink" title="在命令提示符中使用 NULL 值"></a>在命令提示符中使用 NULL 值</h3><p>以下实例中假设数据库 <code>TestDB</code> 中的表 <code>tbl</code> 含有两列 <code>author</code> 和 <code>count</code>, <code>count</code> 中设置插入 <code>NULL</code> 值。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM tbl;</span><br><span class="line">+<span class="comment">---------------+--------------+</span></span><br><span class="line">| author | count |</span><br><span class="line">+<span class="comment">---------------+--------------+</span></span><br><span class="line">| TestDB        | NULL         |</span><br><span class="line">| Google        | NULL         |</span><br><span class="line">| FK            | 20           |</span><br><span class="line">| Baidu         | 20           |</span><br><span class="line">+<span class="comment">---------------+--------------+</span></span><br><span class="line">4 rows in <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br></pre></td></tr></table></figure></p><p>以下实例中你可以看到 <code>=</code> 和 <code>!=</code> 运算符是不起作用的：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM tbl WHERE count = NULL;</span><br><span class="line">Empty <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br><span class="line">mysql&gt; <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> tbl <span class="keyword">WHERE</span> <span class="keyword">count</span> != <span class="literal">NULL</span>;</span><br><span class="line">Empty <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br></pre></td></tr></table></figure></p><p>查找数据表中 <code>tbl</code> 列是否为 <code>NULL</code>，必须使用 <code>IS NULL</code> 和 <code>IS NOT NULL</code>，如下实例：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM tbl WHERE count IS NULL;</span><br><span class="line">+<span class="comment">--------+-------+</span></span><br><span class="line">| author | count |</span><br><span class="line">+<span class="comment">--------+-------+</span></span><br><span class="line">| TestDB | NULL  |</span><br><span class="line">| Google | NULL  |</span><br><span class="line">+<span class="comment">---- ---+-------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> tbl <span class="keyword">WHERE</span> <span class="keyword">count</span> <span class="keyword">IS</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>;</span><br><span class="line">+<span class="comment">--------+-------+</span></span><br><span class="line">| author | count |</span><br><span class="line">+<span class="comment">--------+-------+</span></span><br><span class="line">| FK     | 20    |</span><br><span class="line">| Baidu  | 20    |</span><br><span class="line">+<span class="comment">--------+-------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br></pre></td></tr></table></figure></p><h2 id="正则表达式"><a href="#正则表达式" class="headerlink" title="正则表达式"></a>正则表达式</h2><p>在前面的章节我们已经了解到 MySQL 可以通过 <code>LIKE</code> 子句来进行模糊匹配。</p><p>MySQL 同样也支持其他正则表达式的匹配， MySQL 中使用 <code>REGEXP</code> 操作符来进行正则表达式匹配。</p><p>下表中的正则模式可应用于 <code>REGEXP</code> 操作符中。</p><div class="table-container"><table><thead><tr><th style="text-align:center">模式</th><th style="text-align:center">描述</th></tr></thead><tbody><tr><td style="text-align:center">^</td><td style="text-align:center">匹配输入字符串的开始位置。<br>如果设置了 RegExp 对象的 Multiline 属性，<code>^</code> 也匹配 <code>\n</code> 或 <code>\r</code> 之后的位置。</td></tr><tr><td style="text-align:center">$</td><td style="text-align:center">匹配输入字符串的结束位置。<br>如果设置了 RegExp 对象的 Multiline 属性，<code>$</code> 也匹配 <code>\n</code> 或 <code>\r</code> 之前的位置。</td></tr><tr><td style="text-align:center">.</td><td style="text-align:center">匹配除 <code>\n</code> 之外的任何单个字符。<br>要匹配包括 <code>\n</code> 在内的任何字符，请使用像 <code>[.\n]</code> 的模式。</td></tr><tr><td style="text-align:center">[...]</td><td style="text-align:center">字符集合。匹配所包含的任意一个字符。<br>例如，<code>[abc]</code> 可以匹配 <code>plain</code> 中的 <code>a</code>。</td></tr><tr><td style="text-align:center"><html>[^...]</html></td><td style="text-align:center">负值字符集合。匹配未包含的任意字符。<br>例如，<code>[\^abc]</code> 可以匹配 <code>plain</code> 中的<code>p</code>。</td></tr><tr><td style="text-align:center">p1&vert;p2&vert;p3</td><td style="text-align:center">匹配 p1 或 p2 或 p3。<br>例如，<code>z</code>&vert;<code>food</code> 能匹配 <code>z</code> 或 <code>food</code>。<code>(z</code>&vert;<code>f)ood</code> 则匹配 <code>zood</code> 或 <code>food</code>。</td></tr><tr><td style="text-align:center">*</td><td style="text-align:center">匹配前面的子表达式零次或多次。<br>例如，<code>zo*</code> 能匹配 <code>z</code> 以及 <code>zoo</code>。<code>*</code> 等价于{0,}。</td></tr><tr><td style="text-align:center">+</td><td style="text-align:center">匹配前面的子表达式一次或多次。<br>例如，<code>zo+</code> 能匹配 <code>zo</code> 以及 <code>zoo</code>，但不能匹配 <code>z</code>。<code>+</code> 等价于 <code>&#123;1,&#125;</code>。</td></tr><tr><td style="text-align:center">{n}</td><td style="text-align:center"><code>n</code> 是一个非负整数。匹配确定的 <code>n</code> 次。<br>例如，<code>o&#123;2&#125;</code> 不能匹配 <code>Bob</code> 中的 <code>o</code>，但是能匹配 <code>food</code> 中的两个 <code>o</code>。</td></tr><tr><td style="text-align:center">{n, m}</td><td style="text-align:center"><code>m</code> 和 <code>n</code> 均为非负整数，其中 <code>n &lt;= m</code>。<br>最少匹配 <code>n</code> 次且最多匹配 <code>m</code> 次。</td></tr></tbody></table></div><p>了解以上的正则需求后，我们就可以根据自己的需求来编写带有正则表达式的 SQL 语句。以下我们将列出几个小实例（表名：<code>person_tbl</code>）来加深我们的理解：</p><p>查找 <code>name</code> 字段中以 <code>st</code> 为开头的所有数据：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT name FROM person_tbl WHERE name REGEXP &#x27;^st&#x27;;</span><br></pre></td></tr></table></figure></p><p>查找 <code>name</code> 字段中以 <code>ok</code> 为结尾的所有数据：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT name FROM person_tbl WHERE name REGEXP &#x27;ok$&#x27;;</span><br></pre></td></tr></table></figure></p><p>查找 <code>name</code> 字段中包含 <code>mar</code> 字符串的所有数据：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT name FROM person_tbl WHERE name REGEXP &#x27;mar&#x27;;</span><br></pre></td></tr></table></figure></p><p>查找 <code>name</code> 字段中以元音字符开头或以 <code>ok</code> 字符串结尾的所有数据：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT name FROM person_tbl WHERE name REGEXP &#x27;^[aeiou]|ok$&#x27;;</span><br></pre></td></tr></table></figure></p><h2 id="ALTER-命令"><a href="#ALTER-命令" class="headerlink" title="ALTER 命令"></a>ALTER 命令</h2><p>当我们需要修改数据表名或者修改数据表字段时，就需要使用到 <code>ALTER</code> 命令。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SHOW COLUMNS FROM testalter_tbl;</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">| Field | Type    | Null | Key | Default | Extra |</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">| i     | int(11) | YES  |     | NULL    |       |</span><br><span class="line">| c     | char(1) | YES  |     | NULL    |       |</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><h3 id="删除，添加或修改表字段"><a href="#删除，添加或修改表字段" class="headerlink" title="删除，添加或修改表字段"></a>删除，添加或修改表字段</h3><p>如下命令使用了 <code>ALTER</code> 命令及 <code>DROP</code> 子句来删除以上创建表的 <code>i</code> 字段：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl  DROP i;</span><br></pre></td></tr></table></figure></p><p>如果数据表中只剩余一个字段则无法使用 <code>DROP</code> 来删除字段。</p><p>MySQL 中使用 <code>ADD</code> 子句来向数据表中添加列，如下实例在表 <code>testalter_tbl</code> 中添加 <code>i</code> 字段，并定义数据类型:</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl ADD i INT;</span><br></pre></td></tr></table></figure></p><p>执行以上命令后，<code>i</code> 字段会自动添加到数据表字段的末尾。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SHOW COLUMNS FROM testalter_tbl;</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">| Field | Type    | Null | Key | Default | Extra |</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">| c     | char(1) | YES  |     | NULL    |       |</span><br><span class="line">| i     | int(11) | YES  |     | NULL    |       |</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p>如果你需要指定新增字段的位置，可以使用 MySQL 提供的关键字 <code>FIRST</code> (设定位第一列)， <code>AFTER</code> 字段名（设定位于某个字段之后）。</p><p>尝试以下 <code>ALTER TABLE</code> 语句, 在执行成功后，使用 <code>SHOW COLUMNS</code> 查看表结构的变化：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> testalter_tbl <span class="keyword">DROP</span> i;</span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> testalter_tbl <span class="keyword">ADD</span> i <span class="built_in">INT</span> <span class="keyword">FIRST</span>;</span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> testalter_tbl <span class="keyword">DROP</span> i;</span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> testalter_tbl <span class="keyword">ADD</span> i <span class="built_in">INT</span> <span class="keyword">AFTER</span> c;</span><br></pre></td></tr></table></figure></p><p><code>FIRST</code> 和 <code>AFTER</code> 关键字可用于 <code>ADD</code> 与 <code>MODIFY</code> 子句，所以如果你想重置数据表字段的位置就需要先使用 <code>DROP</code> 删除字段然后使用 <code>ADD</code> 来添加字段并设置位置。</p><h3 id="修改字段类型及名称"><a href="#修改字段类型及名称" class="headerlink" title="修改字段类型及名称"></a>修改字段类型及名称</h3><p>如果需要修改字段类型及名称, 你可以在 <code>ALTER</code> 命令中使用 <code>MODIFY</code> 或 <code>CHANGE</code> 子句 。</p><p>例如，把字段 <code>c</code> 的类型从 <code>CHAR(1)</code> 改为 <code>CHAR(10)</code>，可以执行以下命令:</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl MODIFY c CHAR(10);</span><br></pre></td></tr></table></figure></p><p>使用 <code>CHANGE</code> 子句, 语法有很大的不同。 在 <code>CHANGE</code> 关键字之后，紧跟着的是你要修改的字段名，然后指定新字段名及类型。尝试如下实例：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl CHANGE i j BIGINT;</span><br><span class="line">mysql&gt; ALTER TABLE testalter_tbl CHANGE j j INT;</span><br></pre></td></tr></table></figure></p><h3 id="ALTER-TABLE-对-NULL-值和默认值的影响"><a href="#ALTER-TABLE-对-NULL-值和默认值的影响" class="headerlink" title="ALTER TABLE 对 NULL 值和默认值的影响"></a>ALTER TABLE 对 NULL 值和默认值的影响</h3><p>当你修改字段时，你可以指定是否包含值或者是否设置默认值。</p><p>以下实例，指定字段 <code>j</code> 为 <code>NOT NULL</code> 且默认值为 <code>100</code> 。</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl </span><br><span class="line">    -&gt; MODIFY j BIGINT NOT NULL DEFAULT 100;</span><br></pre></td></tr></table></figure></p><p>如果你不设置默认值，MySQL 会自动设置该字段默认为 <code>NULL</code>。</p><h3 id="修改字段默认值"><a href="#修改字段默认值" class="headerlink" title="修改字段默认值"></a>修改字段默认值</h3><p>你可以使用 <code>ALTER</code> 来修改字段的默认值，尝试以下实例：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl ALTER i SET DEFAULT 1000;</span><br><span class="line">mysql&gt; SHOW COLUMNS FROM testalter_tbl;</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">| Field | Type    | Null | Key | Default | Extra |</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">| c     | char(1) | YES  |     | NULL    |       |</span><br><span class="line">| i     | int(11) | YES  |     | 1000    |       |</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p>你也可以使用 <code>ALTER</code> 命令及 <code>DROP</code> 子句来删除字段的默认值，如下实例：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl ALTER i DROP DEFAULT;</span><br><span class="line">mysql&gt; SHOW COLUMNS FROM testalter_tbl;</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">| Field | Type    | Null | Key | Default | Extra |</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">| c     | char(1) | YES  |     | NULL    |       |</span><br><span class="line">| i     | int(11) | YES  |     | NULL    |       |</span><br><span class="line">+<span class="comment">-------+---------+------+-----+---------+-------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br><span class="line">Changing a <span class="keyword">Table</span> <span class="keyword">Type</span>:</span><br></pre></td></tr></table></figure></p><p>修改数据表类型，可以使用 <code>ALTER</code> 命令及 <code>TYPE</code> 子句来完成。尝试以下实例，我们将表 <code>testalter_tbl</code> 的类型修改为 <code>MyISAM</code>：</p><p>注意：查看数据表类型可以使用 <code>SHOW TABLE STATUS</code> 语句，并使用 <code>\G</code> 格式化输出。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl ENGINE = MyISAM;</span><br><span class="line">mysql&gt; SHOW TABLE STATUS LIKE &#x27;testalter_tbl&#x27; \G</span><br><span class="line">*************************** 1. row ****************</span><br><span class="line">           Name: testalter_tbl</span><br><span class="line">           Type: MyISAM</span><br><span class="line">     Row_format: Fixed</span><br><span class="line">           Rows: 0</span><br><span class="line"> Avg_row_length: 0</span><br><span class="line">    Data_length: 0</span><br><span class="line">Max_data_length: 25769803775</span><br><span class="line">   Index_length: 1024</span><br><span class="line">      Data_free: 0</span><br><span class="line"> Auto_increment: NULL</span><br><span class="line">    Create_time: 2007-06-03 08:04:36</span><br><span class="line">    Update_time: 2007-06-03 08:04:36</span><br><span class="line">     Check_time: NULL</span><br><span class="line"> Create_options:</span><br><span class="line">        <span class="keyword">Comment</span>:</span><br><span class="line"><span class="number">1</span> <span class="keyword">row</span> <span class="keyword">in</span> <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><h3 id="修改表名"><a href="#修改表名" class="headerlink" title="修改表名"></a>修改表名</h3><p>如果需要修改数据表的名称，可以在 <code>ALTER TABLE</code> 语句中使用 <code>RENAME</code> 子句来实现。</p><p>尝试以下实例将数据表 <code>testalter_tbl</code> 重命名为 <code>alter_tbl</code>：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl RENAME TO alter_tbl;</span><br></pre></td></tr></table></figure></p><p><code>ALTER</code> 命令还可以用来创建及删除 MySQL 数据表的索引，该功能我们会在接下来的章节中介绍。</p><h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><p>MySQL 事务主要用于处理操作量大，复杂度高的数据。比如说，在人员管理系统中，你删除一个人员，你既需要删除人员的基本资料，也要删除和该人员相关的信息，如信箱，文章等等，这样，这些数据库操作语句就构成一个事务。</p><ul><li>在 MySQL 中只有使用了 <code>InnoDB</code> 数据库引擎的数据库或表才支持事务。</li><li>事务处理可以用来维护数据库的完整性，保证成批的 SQL 语句要么全部执行，要么全部不执行。</li><li>事务用来管理 <code>INSERT</code>，<code>UPDATE</code>, <code>DELETE</code> 语句。</li></ul><p>一般来说，事务是必须满足 4 个条件（<code>ACID</code>）：原子性（Atomicity，或称不可分割性）、一致性（Consistency）、隔离性（Isolation，又称独立性）、持久性（Durability）。</p><ul><li>原子性：一个事务（transaction）中的所有操作，要么全部完成，要么全部不完成，不会结束在中间某个环节。事务在执行过程中发生错误，会被回滚（Rollback）到事务开始前的状态，就像这个事务从来没有执行过一样。</li><li>一致性：在事务开始之前和事务结束以后，数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则，这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。</li><li>隔离性：数据库允许多个并发事务同时对其数据进行读写和修改的能力，隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别，包括读未提交（Read uncommitted）、读提交（read committed）、可重复读（repeatable read）和串行化（Serializable）。</li><li>持久性：事务处理结束后，对数据的修改就是永久的，即便系统故障也不会丢失。</li></ul><p>在 MySQL 命令行的默认设置下，事务都是自动提交的，即执行 SQL 语句后就会马上执行 <code>COMMIT</code> 操作。因此要显式地开启一个事务务须使用命令 <code>BEGIN</code> 或 <code>START TRANSACTION</code>，或者执行命令 <code>SET AUTOCOMMIT=0</code>，用来禁止使用当前会话的自动提交。</p><p>事务控制语句：</p><ul><li><code>BEGIN</code> 或 <code>START TRANSACTION</code> 显式地开启一个事务；</li><li><code>COMMIT</code> 也可以使用 <code>COMMIT WORK</code>，不过二者是等价的。<code>COMMIT</code> 会提交事务，并使已对数据库进行的所有修改成为永久性的；</li><li><code>ROLLBACK</code> 也可以使用 <code>ROLLBACK WORK</code>，不过二者是等价的。回滚会结束用户的事务，并撤销正在进行的所有未提交的修改；</li><li><code>SAVEPOINT identifier</code>，<code>SAVEPOINT</code> 允许在事务中创建一个保存点，一个事务中可以有多个 <code>SAVEPOINT</code>；</li><li><code>RELEASE SAVEPOINT identifier</code> 删除一个事务的保存点，当没有指定的保存点时，执行该语句会抛出一个异常；</li><li><code>ROLLBACK TO identifier</code> 把事务回滚到标记点；</li><li><code>SET TRANSACTION</code> 用来设置事务的隔离级别。<code>InnoDB</code> 存储引擎提供事务的隔离级别有<code>READ UNCOMMITTED</code>、<code>READ COMMITTED</code>、<code>REPEATABLE READ</code> 和 <code>SERIALIZABLE</code>。</li></ul><p>MYSQL 事务处理主要有两种方法：</p><ol><li><p>用 <code>BEGIN</code>，<code>ROLLBACK</code>，<code>COMMIT</code> 来实现</p><ul><li><code>BEGIN</code>：开始一个事务</li><li><code>ROLLBACK</code>：事务回滚</li><li><code>COMMIT</code>：事务确认</li></ul></li><li><p>直接用 <code>SET</code> 来改变 MySQL 的自动提交模式:</p><ul><li><code>SET AUTOCOMMIT=0</code>：禁止自动提交</li><li><code>SET AUTOCOMMIT=1</code>：开启自动提交</li></ul></li></ol><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE TABLE transaction_test( id int(5)) engine=innodb;  # 创建数据表</span><br><span class="line">Query OK, 0 rows affected (0.04 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; SELECT * FROM transaction_test;</span><br><span class="line">Empty <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; <span class="keyword">BEGIN</span>;  <span class="comment"># 开始事务</span></span><br><span class="line">Query OK, 0 rows affected (0.00 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; INSERT INTO transaction_test VALUE(5);</span><br><span class="line">Query OK, 1 rows affected (0.01 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; INSERT INTO transaction_test VALUE(6);</span><br><span class="line">Query OK, 1 rows affected (0.00 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; COMMIT;  # 提交事务</span><br><span class="line">Query OK, 0 rows affected (0.01 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; SELECT * FROM transaction_test;</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">| id   |</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">| 5    |</span><br><span class="line">| 6    |</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; <span class="keyword">BEGIN</span>;  <span class="comment"># 开始事务</span></span><br><span class="line">Query OK, 0 rows affected (0.00 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; INSERT INTO transaction_test VALUE(7);</span><br><span class="line">Query OK, 1 rows affected (0.00 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; rollback;  # 回滚</span><br><span class="line">Query OK, 0 rows affected (0.00 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; SELECT * FROM transaction_test;  # 因为回滚所以数据没有插入</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">| id   |</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">| 5    |</span><br><span class="line">| 6    |</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">2 rows in <span class="keyword">set</span> (<span class="number">0.01</span> sec)</span><br></pre></td></tr></table></figure></p><h2 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h2><p>MySQL 索引的建立对于 MySQL 的高效运行是很重要的，索引可以大大提高 MySQL 的检索速度。</p><p>打个比方，如果合理的设计且使用索引的 MySQL 是一辆兰博基尼的话，那么没有设计和使用索引的 MySQL 就是一个人力三轮车。</p><p>拿汉语字典的目录页（索引）打比方，我们可以按拼音、笔画、偏旁部首等排序的目录（索引）快速查找到需要的字。</p><p>索引分单列索引和组合索引。单列索引，即一个索引只包含单个列，一个表可以有多个单列索引，但这不是组合索引。组合索引，即一个索引包含多个列。</p><p>创建索引时，你需要确保该索引是应用在 SQL 查询语句的条件（一般作为 <code>WHERE</code> 子句的条件）。</p><p>实际上，索引也是一张表，该表保存了主键与索引字段，并指向实体表的记录。</p><p>上面都在说使用索引的好处，但过多的使用索引将会造成滥用。因此索引也会有它的缺点：虽然索引大大提高了查询速度，同时却会降低更新表的速度，如对表进行 <code>INSERT</code>、<code>UPDATE</code> 和 <code>DELETE</code>。因为更新表时，MySQL 不仅要保存数据，还要保存一下索引文件。</p><p>建立索引会占用磁盘空间的索引文件。</p><h3 id="普通索引"><a href="#普通索引" class="headerlink" title="普通索引"></a>普通索引</h3><h4 id="创建索引"><a href="#创建索引" class="headerlink" title="创建索引"></a>创建索引</h4><p>这是最基本的索引，它没有任何限制。它有以下几种创建方式：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">INDEX</span> indexName <span class="keyword">ON</span> table_name (column_name);</span><br></pre></td></tr></table></figure></p><p>如果是 <code>CHAR</code>，<code>VARCHAR</code> 类型，<code>length</code> 可以小于字段实际长度；如果是 <code>BLOB</code> 和 <code>TEXT</code> 类型，必须指定 <code>length</code>。</p><h4 id="修改表结构-添加索引"><a href="#修改表结构-添加索引" class="headerlink" title="修改表结构(添加索引)"></a>修改表结构(添加索引)</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">table</span> tableName <span class="keyword">ADD</span> <span class="keyword">INDEX</span> indexName(columnName);</span><br></pre></td></tr></table></figure></p><h4 id="创建表的时候直接指定"><a href="#创建表的时候直接指定" class="headerlink" title="创建表的时候直接指定"></a>创建表的时候直接指定</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> mytable(  </span><br><span class="line"><span class="keyword">ID</span> <span class="built_in">INT</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">username <span class="built_in">VARCHAR</span>(<span class="number">16</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line"><span class="keyword">INDEX</span> [indexName] (username(<span class="keyword">length</span>))</span><br><span class="line">);</span><br></pre></td></tr></table></figure></p><h4 id="删除索引的语法"><a href="#删除索引的语法" class="headerlink" title="删除索引的语法"></a>删除索引的语法</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">INDEX</span> [indexName] <span class="keyword">ON</span> mytable;</span><br></pre></td></tr></table></figure></p><h3 id="唯一索引"><a href="#唯一索引" class="headerlink" title="唯一索引"></a>唯一索引</h3><p>它与前面的普通索引类似，不同的就是：索引列的值必须唯一，但允许有空值。如果是组合索引，则列值的组合必须唯一。它有以下几种创建方式：</p><h4 id="创建索引-1"><a href="#创建索引-1" class="headerlink" title="创建索引"></a>创建索引</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">UNIQUE</span> <span class="keyword">INDEX</span> indexName <span class="keyword">ON</span> mytable(username(<span class="keyword">length</span>));</span><br></pre></td></tr></table></figure></p><h4 id="修改表结构"><a href="#修改表结构" class="headerlink" title="修改表结构"></a>修改表结构</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">table</span> mytable <span class="keyword">ADD</span> <span class="keyword">UNIQUE</span> [indexName] (username(<span class="keyword">length</span>));</span><br></pre></td></tr></table></figure></p><h4 id="创建表的时候直接指定-1"><a href="#创建表的时候直接指定-1" class="headerlink" title="创建表的时候直接指定"></a>创建表的时候直接指定</h4><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> mytable(</span><br><span class="line"><span class="keyword">ID</span> <span class="built_in">INT</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">username <span class="built_in">VARCHAR</span>(<span class="number">16</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line"><span class="keyword">UNIQUE</span> [indexName] (username(<span class="keyword">length</span>))</span><br><span class="line">);</span><br></pre></td></tr></table></figure></p><h3 id="使用-ALTER-命令添加和删除索引"><a href="#使用-ALTER-命令添加和删除索引" class="headerlink" title="使用 ALTER 命令添加和删除索引"></a>使用 ALTER 命令添加和删除索引</h3><p>有四种方式来添加数据表的索引：</p><ul><li><code>ALTER TABLE tbl_name ADD PRIMARY KEY (column_list)</code>：该语句添加一个主键，这意味着索引值必须是唯一的，且不能为 <code>NULL</code>。</li><li><code>ALTER TABLE tbl_name ADD UNIQUE index_name (column_list)</code>：这条语句创建索引的值必须是唯一的（除了 <code>NULL</code> 外，<code>NULL</code> 可能会出现多次）。</li><li><code>ALTER TABLE tbl_name ADD INDEX index_name (column_list)</code>：添加普通索引，索引值可出现多次。</li><li><code>ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list)</code>：该语句指定了索引为 <code>FULLTEXT</code>，用于全文索引。</li></ul><p>以下实例为在表中添加索引。</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl ADD INDEX (c);</span><br></pre></td></tr></table></figure></p><p>你还可以在 <code>ALTER</code> 命令中使用 <code>DROP</code> 子句来删除索引。尝试以下实例删除索引:</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl DROP INDEX c;</span><br></pre></td></tr></table></figure></p><h3 id="使用-ALTER-命令添加和删除主键"><a href="#使用-ALTER-命令添加和删除主键" class="headerlink" title="使用 ALTER 命令添加和删除主键"></a>使用 ALTER 命令添加和删除主键</h3><p>主键作用于列上（可以一个列或多个列联合主键），添加主键索引时，你需要确保该主键默认不为空（<code>NOT NULL</code>）。实例如下：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl MODIFY i INT NOT NULL;</span><br><span class="line">mysql&gt; ALTER TABLE testalter_tbl ADD PRIMARY KEY (i);</span><br></pre></td></tr></table></figure></p><p>你也可以使用 <code>ALTER</code> 命令删除主键：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE testalter_tbl DROP PRIMARY KEY;</span><br></pre></td></tr></table></figure></p><p>删除主键时只需指定 <code>PRIMARY KEY</code>，但在删除索引时，你必须知道索引名。</p><h3 id="显示索引信息"><a href="#显示索引信息" class="headerlink" title="显示索引信息"></a>显示索引信息</h3><p>你可以使用 <code>SHOW INDEX</code> 命令来列出表中的相关的索引信息。可以通过添加 <code>\G</code> 来格式化输出信息。</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SHOW INDEX FROM table_name; \G</span><br></pre></td></tr></table></figure></p><h2 id="临时表"><a href="#临时表" class="headerlink" title="临时表"></a>临时表</h2><p>MySQL 临时表在我们需要保存一些临时数据时是非常有用的。临时表只在当前连接可见，当关闭连接时，MySQL 会自动删除表并释放所有空间。</p><p>临时表在 MySQL 3.23 版本中添加，如果你的 MySQL 版本低于 3.23 版本就无法使用 MySQL 的临时表。不过现在一般很少有再使用这么低版本的 MySQL 数据库服务了。</p><p>MySQL 临时表只在当前连接可见，如果你使用 PHP 脚本来创建 MySQL 临时表，那每当 PHP 脚本执行完成后，该临时表也会自动销毁。</p><p>如果你使用了其他 MySQL 客户端程序连接 MySQL 数据库服务器来创建临时表，那么只有在关闭客户端程序时才会销毁临时表，当然你也可以手动销毁。</p><h3 id="创建-MySQL-临时表"><a href="#创建-MySQL-临时表" class="headerlink" title="创建 MySQL 临时表"></a>创建 MySQL 临时表</h3><p>以下展示了使用 <code>TEMPORARY</code> 关键字创建 MySQL 临时表的简单实例：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE TEMPORARY TABLE SalesSummary (</span><br><span class="line">    -&gt; product_name VARCHAR(50) NOT NULL,</span><br><span class="line">    -&gt; total_sales DECIMAL(12, 2) NOT NULL DEFAULT 0.00,</span><br><span class="line">    -&gt; avg_unit_price DECIMAL(7, 2) NOT NULL DEFAULT 0.00,</span><br><span class="line">    -&gt; total_units_sold INT UNSIGNED NOT NULL DEFAULT 0</span><br><span class="line">);</span><br><span class="line">Query OK, 0 rows affected (0.00 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; INSERT INTO SalesSummary</span><br><span class="line">    -&gt; (product_name, total_sales, avg_unit_price, total_units_sold)</span><br><span class="line">    -&gt; VALUES</span><br><span class="line">    -&gt; (&#x27;cucumber&#x27;, 100.25, 90, 2);</span><br><span class="line"></span><br><span class="line">mysql&gt; SELECT * FROM SalesSummary;</span><br><span class="line">+<span class="comment">--------------+-------------+----------------+------------------+</span></span><br><span class="line">| product_name | total_sales | avg_unit_price | total_units_sold |</span><br><span class="line">+<span class="comment">--------------+-------------+----------------+------------------+</span></span><br><span class="line">| cucumber     |      100.25 |          90.00 |                2 |</span><br><span class="line">+<span class="comment">--------------+-------------+----------------+------------------+</span></span><br><span class="line">1 row in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><p>用查询直接创建临时表：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TEMPORARY</span> <span class="keyword">TABLE</span> 临时表名 <span class="keyword">AS</span></span><br><span class="line">(</span><br><span class="line">    <span class="keyword">SELECT</span> *  <span class="keyword">FROM</span> 旧的表名</span><br><span class="line">);</span><br></pre></td></tr></table></figure></p><p>当你使用 <code>SHOW TABLES</code> 命令显示数据表列表时，你将无法看到 <code>SalesSummary</code> 表。</p><p>如果你退出当前 MySQL 会话，再使用 <code>SELECT</code> 命令来读取原先创建的临时表数据，那你会发现数据库中没有该表的存在，因为在你退出时该临时表已经被销毁了。</p><h3 id="删除-MySQL-临时表"><a href="#删除-MySQL-临时表" class="headerlink" title="删除 MySQL 临时表"></a>删除 MySQL 临时表</h3><p>默认情况下，当你断开与数据库的连接后，临时表就会自动被销毁。当然你也可以在当前 MySQL 会话使用 <code>DROP TABLE</code> 命令来手动删除临时表。</p><p>以下是手动删除临时表的实例：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE TEMPORARY TABLE SalesSummary (</span><br><span class="line">    -&gt; product_name VARCHAR(50) NOT NULL,</span><br><span class="line">    -&gt; total_sales DECIMAL(12, 2) NOT NULL DEFAULT 0.00,</span><br><span class="line">    -&gt; avg_unit_price DECIMAL(7, 2) NOT NULL DEFAULT 0.00,</span><br><span class="line">    -&gt; total_units_sold INT UNSIGNED NOT NULL DEFAULT 0,</span><br><span class="line">);</span><br><span class="line">Query OK, 0 rows affected (0.00 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; INSERT INTO SalesSummary</span><br><span class="line">    -&gt; (product_name, total_sales, avg_unit_price, total_units_sold)</span><br><span class="line">    -&gt; VALUES</span><br><span class="line">    -&gt; (&#x27;cucumber&#x27;, 100.25, 90, 2);</span><br><span class="line"></span><br><span class="line">mysql&gt; SELECT * FROM SalesSummary;</span><br><span class="line">+<span class="comment">--------------+-------------+----------------+------------------+</span></span><br><span class="line">| product_name | total_sales | avg_unit_price | total_units_sold |</span><br><span class="line">+<span class="comment">--------------+-------------+----------------+------------------+</span></span><br><span class="line">| cucumber     |      100.25 |          90.00 |                2 |</span><br><span class="line">+<span class="comment">--------------+-------------+----------------+------------------+</span></span><br><span class="line">1 row in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; <span class="keyword">DROP</span> <span class="keyword">TABLE</span> SalesSummary;</span><br><span class="line">mysql&gt;  SELECT * FROM SalesSummary;</span><br><span class="line">ERROR 1146: Table &#x27;TestDB.SalesSummary&#x27; doesn&#x27;t exist</span><br></pre></td></tr></table></figure></p><h2 id="复制表"><a href="#复制表" class="headerlink" title="复制表"></a>复制表</h2><p>如果我们需要完全的复制 MySQL 的数据表，包括表的结构，索引，默认值等。如果仅仅使用<code>CREATE TABLE ... SELECT</code> 命令，是无法实现的。</p><p>完整的复制 MySQL 数据表的步骤如下：</p><ol><li>使用 <code>SHOW CREATE TABLE</code> 命令获取创建数据表（<code>CREATE TABLE</code>）语句，该语句包含了原数据表的结构，索引等。</li><li>复制以下命令显示的 SQL 语句，修改数据表名，并执行 SQL 语句，通过以上命令将完全的复制数据表结构。</li><li>如果你想复制表的内容，你就可以使用 <code>INSERT INTO ... SELECT</code> 语句来实现。</li></ol><p>尝试以下实例来复制表 <code>tbl</code>。</p><ol><li><p>获取数据表的完整结构。</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SHOW CREATE TABLE tbl \G;</span><br><span class="line">*************************** 1. row ***************************</span><br><span class="line">      Table: tbl</span><br><span class="line"><span class="keyword">Create</span> <span class="keyword">Table</span>: <span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="string">`tbl`</span> (</span><br><span class="line">  <span class="string">`id`</span> <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> auto_increment,</span><br><span class="line">  <span class="string">`title`</span> <span class="built_in">varchar</span>(<span class="number">100</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">default</span> <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">  <span class="string">`author`</span> <span class="built_in">varchar</span>(<span class="number">40</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">default</span> <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">  <span class="string">`submission_date`</span> <span class="built_in">date</span> <span class="keyword">default</span> <span class="literal">NULL</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span>  (<span class="string">`id`</span>),</span><br><span class="line">  <span class="keyword">UNIQUE</span> <span class="keyword">KEY</span> <span class="string">`AUTHOR_INDEX`</span> (<span class="string">`author`</span>)</span><br><span class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> </span><br><span class="line"><span class="number">1</span> <span class="keyword">row</span> <span class="keyword">in</span> <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br><span class="line"></span><br><span class="line"><span class="keyword">ERROR</span>:</span><br><span class="line"><span class="keyword">No</span> <span class="keyword">query</span> specified</span><br></pre></td></tr></table></figure></p></li><li><p>修改 SQL 语句的数据表名，并执行 SQL 语句。</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE TABLE `clone_tbl` (</span><br><span class="line">  -&gt; `id` int(11) NOT NULL auto_increment,</span><br><span class="line">  -&gt; `title` varchar(100) NOT NULL default &#x27;&#x27;,</span><br><span class="line">  -&gt; `author` varchar(40) NOT NULL default &#x27;&#x27;,</span><br><span class="line">  -&gt; `submission_date` date default NULL,</span><br><span class="line">  -&gt; PRIMARY KEY  (`id`),</span><br><span class="line">  -&gt; UNIQUE KEY `AUTHOR_INDEX` (`author`)</span><br><span class="line">  -&gt; ) ENGINE=InnoDB;</span><br><span class="line">Query OK, 0 rows affected (1.80 sec)</span><br></pre></td></tr></table></figure></p></li><li><p>执行完第二步骤后，你将在数据库中创建新的克隆表 clone_tbl。 如果你想拷贝数据表的数据你可以使用 INSERT INTO... SELECT 语句来实现。</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; INSERT INTO clone_tbl (id,</span><br><span class="line">    -&gt;                        title,</span><br><span class="line">    -&gt;                        author,</span><br><span class="line">    -&gt;                        submission_date)</span><br><span class="line">    -&gt; SELECT id,title, author, submission_date</span><br><span class="line">    -&gt; FROM tbl;</span><br><span class="line">Query OK, 3 rows affected (0.07 sec)</span><br><span class="line">Records: 3  Duplicates: 0  Warnings: 0</span><br></pre></td></tr></table></figure></p></li></ol><p>执行以上步骤后，你将完整的复制表，包括表结构及表数据。</p><p><strong>另一种完整复制表的方法:</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> targetTable <span class="keyword">LIKE</span> sourceTable;</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> targetTable <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> sourceTable;</span><br></pre></td></tr></table></figure></p><p><strong>可以拷贝一个表中其中的一些字段:</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> newadmin <span class="keyword">AS</span> (<span class="keyword">SELECT</span> username, <span class="keyword">password</span> <span class="keyword">FROM</span> <span class="keyword">admin</span>);</span><br></pre></td></tr></table></figure></p><p><strong>可以将新建的表的字段改名:</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> newadmin <span class="keyword">AS</span> (<span class="keyword">SELECT</span> <span class="keyword">id</span>, username <span class="keyword">AS</span> uname, <span class="keyword">password</span> <span class="keyword">AS</span> pass <span class="keyword">FROM</span> <span class="keyword">admin</span>);</span><br></pre></td></tr></table></figure></p><p><strong>可以拷贝一部分数据:</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> newadmin <span class="keyword">AS</span> (<span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">admin</span> <span class="keyword">WHERE</span> <span class="keyword">LEFT</span>(username, <span class="number">1</span>) = <span class="string">&#x27;s&#x27;</span>);</span><br></pre></td></tr></table></figure></p><p><strong>可以在创建表的同时定义表中的字段信息:</strong></p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> newadmin (<span class="keyword">id</span> <span class="built_in">INTEGER</span> <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT PRIMARY <span class="keyword">KEY</span>) <span class="keyword">AS</span> (<span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">admin</span>);</span><br></pre></td></tr></table></figure></p><h2 id="元数据"><a href="#元数据" class="headerlink" title="元数据"></a>元数据</h2><ul><li>查询结果信息：<code>SELECT</code>，<code>UPDATE</code> 或 <code>DELETE</code> 语句影响的记录数。</li><li>数据库和数据表的信息：包含了数据库及数据表的结构信息。</li><li>MySQL 服务器信息：包含了数据库服务器的当前状态，版本号等。</li></ul><p>在 MySQL 的命令提示符中，我们可以很容易的获取以上服务器信息。</p><h3 id="获取服务器元数据"><a href="#获取服务器元数据" class="headerlink" title="获取服务器元数据"></a>获取服务器元数据</h3><p>以下命令语句可以在 MySQL 的命令提示符使用，也可以在脚本中使用。</p><div class="table-container"><table><thead><tr><th style="text-align:center">命令</th><th style="text-align:center">描述</th></tr></thead><tbody><tr><td style="text-align:center">SELECT VERSION( )</td><td style="text-align:center">服务器版本信息</td></tr><tr><td style="text-align:center">SELECT DATABASE( )</td><td style="text-align:center">当前数据库名（或者返回空）</td></tr><tr><td style="text-align:center">SELECT USER( )</td><td style="text-align:center">当前用户名</td></tr><tr><td style="text-align:center">SHOW STATUS</td><td style="text-align:center">服务器状态</td></tr><tr><td style="text-align:center">SHOW VARIABLES</td><td style="text-align:center">服务器配置变量</td></tr></tbody></table></div><h2 id="序列使用"><a href="#序列使用" class="headerlink" title="序列使用"></a>序列使用</h2><p>MySQL 序列是一组整数：<code>1, 2, 3, ...</code>，由于一张数据表只能有一个字段自增主键，如果你想实现其他字段也实现自动增加，就可以使用 MySQL 序列来实现。</p><h3 id="使用-AUTO-INCREMENT"><a href="#使用-AUTO-INCREMENT" class="headerlink" title="使用 AUTO_INCREMENT"></a>使用 AUTO_INCREMENT</h3><p>MySQL 中最简单使用序列的方法就是使用 <code>AUTO_INCREMENT</code> 来定义列。</p><p>以下实例中创建了数据表 <code>insect</code>，<code>insect</code> 表中 <code>id</code> 无需指定值可实现自动增长。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE TABLE insect</span><br><span class="line">    -&gt; (</span><br><span class="line">    -&gt; id INT UNSIGNED NOT NULL AUTO_INCREMENT,</span><br><span class="line">    -&gt; PRIMARY KEY (id),</span><br><span class="line">    -&gt; name VARCHAR(30) NOT NULL, # type of insect</span><br><span class="line">    -&gt; date DATE NOT NULL, # date collected</span><br><span class="line">    -&gt; origin VARCHAR(30) NOT NULL # where collected</span><br><span class="line">);</span><br><span class="line">Query OK, 0 rows affected (0.02 sec)</span><br><span class="line"></span><br><span class="line">mysql&gt; INSERT INTO insect (id, name, date, origin) VALUES</span><br><span class="line">    -&gt; (NULL, &#x27;housefly&#x27;, &#x27;2001-09-10&#x27;, &#x27;kitchen&#x27;),</span><br><span class="line">    -&gt; (NULL, &#x27;millipede&#x27;, &#x27;2001-09-10&#x27;, &#x27;driveway&#x27;),</span><br><span class="line">    -&gt; (NULL, &#x27;grasshopper&#x27;, &#x27;2001-09-10&#x27;, &#x27;front yard&#x27;);</span><br><span class="line">Query OK, 3 rows affected (0.02 sec)</span><br><span class="line">Records: 3  Duplicates: 0  Warnings: 0</span><br><span class="line"></span><br><span class="line">mysql&gt; SELECT * FROM insect ORDER BY id;</span><br><span class="line">+<span class="comment">----+-------------+------------+------------+</span></span><br><span class="line">| id | name        | date       | origin     |</span><br><span class="line">+<span class="comment">----+-------------+------------+------------+</span></span><br><span class="line">|  1 | housefly    | 2001-09-10 | kitchen    |</span><br><span class="line">|  2 | millipede   | 2001-09-10 | driveway   |</span><br><span class="line">|  3 | grasshopper | 2001-09-10 | front yard |</span><br><span class="line">+<span class="comment">----+-------------+------------+------------+</span></span><br><span class="line">3 rows in <span class="keyword">set</span> (<span class="number">0.00</span> sec)</span><br></pre></td></tr></table></figure></p><h3 id="获取-AUTO-INCREMENT-值"><a href="#获取-AUTO-INCREMENT-值" class="headerlink" title="获取 AUTO_INCREMENT 值"></a>获取 AUTO_INCREMENT 值</h3><p>在 MySQL 的客户端中你可以使用 SQL 中的 <code>LAST_INSERT_ID( )</code> 函数来获取最后的插入表中的自增列的值。</p><h3 id="重置序列"><a href="#重置序列" class="headerlink" title="重置序列"></a>重置序列</h3><p>如果你删除了数据表中的多条记录，并希望对剩下数据的 <code>AUTO_INCREMENT</code> 列进行重新排列，那么你可以通过删除自增的列，然后重新添加来实现。不过该操作要非常小心，如果在删除的同时又有新记录添加，有可能会出现数据混乱。操作如下所示：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE insect DROP id;</span><br><span class="line">mysql&gt; ALTER TABLE insect</span><br><span class="line">    -&gt; ADD id INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,</span><br><span class="line">    -&gt; ADD PRIMARY KEY (id);</span><br></pre></td></tr></table></figure></p><h3 id="设置序列的开始值"><a href="#设置序列的开始值" class="headerlink" title="设置序列的开始值"></a>设置序列的开始值</h3><p>一般情况下序列的开始值为 1，但如果你需要指定一个开始值 100，那我们可以通过以下语句来实现：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE TABLE insect</span><br><span class="line">    -&gt; (</span><br><span class="line">    -&gt; id INT UNSIGNED NOT NULL AUTO_INCREMENT,</span><br><span class="line">    -&gt; PRIMARY KEY (id),</span><br><span class="line">    -&gt; name VARCHAR(30) NOT NULL,</span><br><span class="line">    -&gt; date DATE NOT NULL,</span><br><span class="line">    -&gt; origin VARCHAR(30) NOT NULL</span><br><span class="line">)engine=innodb auto_increment=100 charset=utf8;</span><br></pre></td></tr></table></figure></p><p>或者你也可以在表创建成功后，通过以下语句来实现：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER TABLE t AUTO_INCREMENT = 100;</span><br></pre></td></tr></table></figure></p><h2 id="处理重复数据"><a href="#处理重复数据" class="headerlink" title="处理重复数据"></a>处理重复数据</h2><p>有些 MySQL 数据表中可能存在重复的记录，有些情况我们允许重复数据的存在，但有时候我们也需要删除这些重复的数据。</p><p>本章节我们将为大家介绍如何防止数据表出现重复数据及如何删除数据表中的重复数据。</p><h3 id="防止表中出现重复数据"><a href="#防止表中出现重复数据" class="headerlink" title="防止表中出现重复数据"></a>防止表中出现重复数据</h3><p>你可以在 MySQL 数据表中设置指定的字段为 <code>PRIMARY KEY</code>（主键） 或者 <code>UNIQUE</code>（唯一） 索引来保证数据的唯一性。</p><p>让我们尝试一个实例：下表中无索引及主键，所以该表允许出现多条重复记录。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> person_tbl</span><br><span class="line">(</span><br><span class="line">    first_name <span class="built_in">CHAR</span>(<span class="number">20</span>),</span><br><span class="line">    last_name <span class="built_in">CHAR</span>(<span class="number">20</span>),</span><br><span class="line">    sex <span class="built_in">CHAR</span>(<span class="number">10</span>)</span><br><span class="line">);</span><br></pre></td></tr></table></figure></p><p>如果你想设置表中字段 <code>first_name</code>，<code>last_name</code> 数据不能重复，你可以设置双主键模式来设置数据的唯一性，如果你设置了双主键，那么那个键的默认值不能为 <code>NULL</code>，可设置为 <code>NOT NULL</code>。如下所示：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> person_tbl</span><br><span class="line">(</span><br><span class="line">   first_name <span class="built_in">CHAR</span>(<span class="number">20</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">   last_name <span class="built_in">CHAR</span>(<span class="number">20</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">   sex <span class="built_in">CHAR</span>(<span class="number">10</span>),</span><br><span class="line">   PRIMARY <span class="keyword">KEY</span> (last_name, first_name)</span><br><span class="line">);</span><br></pre></td></tr></table></figure></p><p>如果我们设置了唯一索引，那么在插入重复数据时，SQL 语句将无法执行成功，并抛出错误。</p><p><code>INSERT IGNORE INTO</code> 与 <code>INSERT INTO</code> 的区别就是 <code>INSERT IGNORE INTO</code> 会忽略数据库中已经存在的数据，如果数据库没有数据，就插入新的数据，如果有数据的话就跳过这条数据。这样就可以保留数据库中已经存在数据，达到在间隙中插入数据的目的。</p><p>以下实例使用了 <code>INSERT IGNORE INTO</code>，执行后不会出错，也不会向数据表中插入重复数据：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; INSERT IGNORE INTO person_tbl (last_name, first_name)</span><br><span class="line">    -&gt; VALUES( &#x27;Jay&#x27;, &#x27;Thomas&#x27;);</span><br><span class="line">Query OK, 1 row affected (0.00 sec)</span><br><span class="line">mysql&gt; INSERT IGNORE INTO person_tbl (last_name, first_name)</span><br><span class="line">    -&gt; VALUES( &#x27;Jay&#x27;, &#x27;Thomas&#x27;);</span><br><span class="line">Query OK, 0 rows affected (0.00 sec)</span><br></pre></td></tr></table></figure></p><p><code>INSERT IGNORE INTO</code> 当插入数据时，在设置了记录的唯一性后，如果插入重复数据，将不返回错误，只以警告形式返回。而 <code>REPLACE INTO</code> 如果存在 <code>PRIMARY</code> 或 <code>UNIQUE</code> 相同的记录，则先删除掉。再插入新记录。</p><p>另一种设置数据的唯一性方法是添加一个 <code>UNIQUE</code> 索引，如下所示：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> person_tbl</span><br><span class="line">(</span><br><span class="line">   first_name <span class="built_in">CHAR</span>(<span class="number">20</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">   last_name <span class="built_in">CHAR</span>(<span class="number">20</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line">   sex <span class="built_in">CHAR</span>(<span class="number">10</span>),</span><br><span class="line">   <span class="keyword">UNIQUE</span> (last_name, first_name)</span><br><span class="line">);</span><br></pre></td></tr></table></figure></p><h3 id="统计重复数据"><a href="#统计重复数据" class="headerlink" title="统计重复数据"></a>统计重复数据</h3><p>以下我们将统计表中 <code>first_name</code> 和 <code>last_name</code> 的重复记录数：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT COUNT(*) as repetitions, last_name, first_name</span><br><span class="line">    -&gt; FROM person_tbl</span><br><span class="line">    -&gt; GROUP BY last_name, first_name</span><br><span class="line">    -&gt; HAVING repetitions &gt; 1;</span><br></pre></td></tr></table></figure></p><p>以上查询语句将返回 <code>person_tbl</code> 表中重复的记录数。一般情况下，查询重复的值，请执行以下操作：</p><ul><li>确定哪一列包含的值可能会重复。</li><li>在列选择列表使用 <code>COUNT(*)</code> 列出的那些列。</li><li>在 <code>GROUP BY</code> 子句中列出的列。</li><li><code>HAVING</code> 子句设置重复数大于 1。</li></ul><h3 id="过滤重复数据"><a href="#过滤重复数据" class="headerlink" title="过滤重复数据"></a>过滤重复数据</h3><p>如果你需要读取不重复的数据可以在 <code>SELECT</code> 语句中使用 <code>DISTINCT</code> 关键字来过滤重复数据。</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT DISTINCT last_name, first_name</span><br><span class="line">    -&gt; FROM person_tbl;</span><br></pre></td></tr></table></figure></p><p>你也可以使用 <code>GROUP BY</code> 来读取数据表中不重复的数据：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT last_name, first_name</span><br><span class="line">    -&gt; FROM person_tbl</span><br><span class="line">    -&gt; GROUP BY (last_name, first_name);</span><br></pre></td></tr></table></figure></p><h3 id="删除重复数据"><a href="#删除重复数据" class="headerlink" title="删除重复数据"></a>删除重复数据</h3><p>如果你想删除数据表中的重复数据，你可以使用以下的 SQL 语句：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE TABLE tmp</span><br><span class="line">    -&gt; SELECT last_name, first_name, sex</span><br><span class="line">    -&gt; FROM person_tbl</span><br><span class="line">    -&gt; GROUP BY (last_name, first_name, sex);</span><br><span class="line">mysql&gt; DROP TABLE person_tbl;</span><br><span class="line">mysql&gt; ALTER TABLE tmp RENAME TO person_tbl;</span><br></pre></td></tr></table></figure></p><p>当然你也可以在数据表中添加 <code>INDEX</code>（索引） 和 <code>PRIMARY KEY</code>（主键）这种简单的方法来删除表中的重复记录。方法如下：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; ALTER IGNORE TABLE person_tbl</span><br><span class="line">    -&gt; ADD PRIMARY KEY (last_name, first_name);</span><br></pre></td></tr></table></figure></p><h2 id="SQL-注入"><a href="#SQL-注入" class="headerlink" title="SQL 注入"></a>SQL 注入</h2><p>如果您通过网页获取用户输入的数据并将其插入一个 MySQL 数据库，那么就有可能发生 SQL 注入安全的问题。</p><p>本章节将为大家介绍如何防止 SQL 注入，并通过脚本来过滤 SQL 中注入的字符。</p><p>所谓 SQL 注入，就是通过把 SQL 命令插入到 Web 表单递交或输入域名或页面请求的查询字符串，最终达到欺骗服务器执行恶意的 SQL 命令。</p><p>我们永远不要信任用户的输入，我们必须认定用户输入的数据都是不安全的，我们都需要对用户输入的数据进行过滤处理。</p><p>以下实例中，输入的用户名必须为字母、数字及下划线的组合，且用户名长度为 8 到 20 个字符之间：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">if (preg_match(&quot;/^\w&#123;8, 20&#125;$/&quot;, $_GET[&#x27;username&#x27;], $matches))</span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">   $</span><span class="bash">result = mysqli_query(<span class="variable">$conn</span>, <span class="string">&quot;SELECT * FROM users</span></span> </span><br><span class="line">                          WHERE username=$matches[0]&quot;);</span><br><span class="line">&#125;</span><br><span class="line"> else </span><br><span class="line">&#123;</span><br><span class="line">   echo &quot;username 输入异常&quot;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>让我们看下在没有过滤特殊字符时，出现的 SQL 情况：</p><p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 设定 $name 中插入了我们不需要的 SQL 语句</span></span><br><span class="line"><span class="variable">$name</span> = <span class="string">&quot;Qadir&#x27;; DELETE FROM users;&quot;</span>;</span><br><span class="line">mysqli_query(<span class="variable">$conn</span>, <span class="string">&quot;SELECT * FROM users WHERE name=&#x27;<span class="subst">&#123;$name&#125;</span>&#x27;&quot;</span>);</span><br></pre></td></tr></table></figure></p><p>以上的注入语句中，我们没有对 <code>$name</code> 的变量进行过滤，<code>$name</code> 中插入了我们不需要的 SQL 语句，将删除 <code>users</code> 表中的所有数据。</p><p>在 PHP 中的 <code>mysqli_query()</code> 是不允许执行多个 SQL 语句的，但是在 <code>SQLite</code> 和 <code>PostgreSQL</code> 是可以同时执行多条 SQL 语句的，所以我们对这些用户的数据需要进行严格的验证。</p><p><strong>防止 SQL 注入，我们需要注意以下几个要点：</strong></p><ol><li>永远不要信任用户的输入。对用户的输入进行校验，可以通过正则表达式，或限制长度；对单引号和 双&quot;-&quot;进行转换等。</li><li>永远不要使用动态拼装 sql，可以使用参数化的 sql 或者直接使用存储过程进行数据查询存取。</li><li>永远不要使用管理员权限的数据库连接，为每个应用使用单独的权限有限的数据库连接。</li><li>不要把机密信息直接存放，加密或者 hash 掉密码和敏感的信息。</li><li>应用的异常信息应该给出尽可能少的提示，最好使用自定义的错误信息对原始错误信息进行包装</li><li>SQL 注入的检测方法一般采取辅助软件或网站平台来检测，软件一般采用 SQL 注入检测工具 jsky，网站平台就有亿思网站安全平台检测工具。MDCSOFT SCAN 等。采用 MDCSOFT-IPS 可以有效的防御 SQL 注入，XSS 攻击等。</li></ol><h3 id="防止-SQL-注入"><a href="#防止-SQL-注入" class="headerlink" title="防止 SQL 注入"></a>防止 SQL 注入</h3><p>在脚本语言，如 Perl 和 PHP 你可以对用户输入的数据进行转义从而来防止 SQL 注入。</p><p>PHP 的 MySQL 扩展提供了 <code>mysqli_real_escape_string()</code> 函数来转义特殊的输入字符。</p><p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (get_magic_quotes_gpc()) </span><br><span class="line">&#123;</span><br><span class="line">  <span class="variable">$name</span> = stripslashes(<span class="variable">$name</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="variable">$name</span> = mysqli_real_escape_string(<span class="variable">$conn</span>, <span class="variable">$name</span>);</span><br><span class="line"> mysqli_query(<span class="variable">$conn</span>, <span class="string">&quot;SELECT * FROM users WHERE name=&#x27;<span class="subst">&#123;$name&#125;</span>&#x27;&quot;</span>);</span><br></pre></td></tr></table></figure></p><h3 id="LIKE-语句中的注入"><a href="#LIKE-语句中的注入" class="headerlink" title="LIKE 语句中的注入"></a>LIKE 语句中的注入</h3><p><code>LIKE</code> 查询时，如果用户输入的值有 <code>_</code> 和 <code>%</code>，则会出现这种情况：用户本来只是想查询 <code>abcd_</code>，查询结果中却有 <code>abcd_</code>、<code>abcde</code>、<code>abcdf</code> 等等；用户要查询 <code>30%</code>（注：百分之三十）时也会出现问题。</p><p>在 PHP 脚本中我们可以使用 <code>addcslashes()</code> 函数来处理以上情况，如下实例：</p><p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$sub</span> = addcslashes(mysqli_real_escape_string(<span class="variable">$conn</span>, <span class="string">&quot;%something_&quot;</span>), <span class="string">&quot;%_&quot;</span>);</span><br><span class="line"><span class="comment">// $sub == \%something\_</span></span><br><span class="line"> mysqli_query(<span class="variable">$conn</span>, <span class="string">&quot;SELECT * FROM messages WHERE subject LIKE &#x27;<span class="subst">&#123;$sub&#125;</span>%&#x27;&quot;</span>);</span><br></pre></td></tr></table></figure></p><p><code>addcslashes()</code> 函数在指定的字符前添加反斜杠。</p><p>语法格式:</p><p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">addcslashes(<span class="keyword">string</span>, characters)</span><br></pre></td></tr></table></figure></p><h2 id="导出数据"><a href="#导出数据" class="headerlink" title="导出数据"></a>导出数据</h2><p>MySQL 中你可以使用 <code>SELECT...INTO OUTFILE</code> 语句来简单的导出数据到文本文件上。</p><h3 id="使用-SELECT-INTO-OUTFILE-语句导出数据"><a href="#使用-SELECT-INTO-OUTFILE-语句导出数据" class="headerlink" title="使用 SELECT ... INTO OUTFILE 语句导出数据"></a>使用 SELECT ... INTO OUTFILE 语句导出数据</h3><p>以下实例中我们将数据表 <code>tbl</code> 数据导出到 <code>/tmp/TestDB.txt</code> 文件中:</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM tbl </span><br><span class="line">    -&gt; INTO OUTFILE &#x27;/tmp/TestDB.txt&#x27;;</span><br></pre></td></tr></table></figure></p><p>你可以通过命令选项来设置数据输出的指定格式，以下实例为导出 <code>CSV</code> 格式：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT * FROM passwd INTO OUTFILE &#x27;/tmp/TestDB.txt&#x27;</span><br><span class="line">    -&gt; FIELDS TERMINATED BY &#x27;,&#x27; ENCLOSED BY &#x27;&quot;&#x27;</span><br><span class="line">    -&gt; LINES TERMINATED BY &#x27;\r\n&#x27;;</span><br></pre></td></tr></table></figure></p><p>在下面的例子中，生成一个文件，各值用逗号隔开。这种格式可以被许多程序使用。</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> a, b, a+b <span class="keyword">INTO</span> <span class="keyword">OUTFILE</span> <span class="string">&#x27;/tmp/result.text&#x27;</span></span><br><span class="line"><span class="keyword">FIELDS</span> <span class="keyword">TERMINATED</span> <span class="keyword">BY</span> <span class="string">&#x27;,&#x27;</span> <span class="keyword">OPTIONALLY</span> <span class="keyword">ENCLOSED</span> <span class="keyword">BY</span> <span class="string">&#x27;&quot;&#x27;</span></span><br><span class="line"><span class="keyword">LINES</span> <span class="keyword">TERMINATED</span> <span class="keyword">BY</span> <span class="string">&#x27;\n&#x27;</span></span><br><span class="line"><span class="keyword">FROM</span> <span class="keyword">table</span>;</span><br></pre></td></tr></table></figure></p><p><strong><code>SELECT ... INTO OUTFILE</code> 语句有以下属性:</strong></p><ul><li><code>LOAD DATA INFILE</code> 是 <code>SELECT ... INTO OUTFILE</code> 的逆操作，<code>SELECT</code> 句法。为了将一个数据库的数据写入一个文件，使用 <code>SELECT ... INTO OUTFILE</code>；为了将文件读回数据库，使用 <code>LOAD DATA INFILE</code>。</li><li><code>SELECT...INTO OUTFILE &#39;file_name&#39;</code> 形式的 <code>SELECT</code> 可以把被选择的行写入一个文件中。该文件被创建到服务器主机上，因此您必须拥有 <code>FILE</code> 权限，才能使用此语法。</li><li>输出不能是一个已存在的文件。防止文件数据被篡改。</li><li>你需要有一个登陆服务器的账号来检索文件。否则 <code>SELECT ... INTO OUTFILE</code> 不会起任何作用。</li><li>在 <code>UNIX</code> 中，该文件被创建后是可读的，权限由 MySQL 服务器所拥有。这意味着，虽然你就可以读取该文件，但可能无法将其删除。</li></ul><h3 id="导出表作为原始数据"><a href="#导出表作为原始数据" class="headerlink" title="导出表作为原始数据"></a>导出表作为原始数据</h3><p><code>mysqldump</code> 是 MySQL 用于转存储数据库的实用程序。它主要产生一个 SQL 脚本，其中包含从头重新创建数据库所必需的命令 <code>CREATE TABLE INSERT</code> 等。</p><p>使用 <code>mysqldump</code> 导出数据需要使用 <code>--tab</code> 选项来指定导出文件指定的目录，该目标必须是可写的。</p><p>以下实例将数据表 <code>tbl</code> 导出到 <code>/tmp</code> 目录中：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysqldump -u root -p --no-create-info --tab=/tmp TestDB tbl</span></span><br><span class="line">password ******</span><br></pre></td></tr></table></figure></p><h3 id="导出-SQL-格式的数据"><a href="#导出-SQL-格式的数据" class="headerlink" title="导出 SQL 格式的数据"></a>导出 SQL 格式的数据</h3><p>导出 SQL 格式的数据到指定文件，如下所示：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysqldump -u root -p TestDB tbl &gt; dump.txt</span></span><br><span class="line">password ******</span><br></pre></td></tr></table></figure></p><p>以上命令创建的文件内容如下：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- MySQL dump 8.23</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- Host: localhost    Database: TestDB</span></span><br><span class="line"><span class="comment">---------------------------------------------------------</span></span><br><span class="line"><span class="comment">-- Server version       3.23.58</span></span><br><span class="line"></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- Table structure for table `tbl`</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> tbl (</span><br><span class="line">  <span class="keyword">id</span> <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> auto_increment,</span><br><span class="line">  title <span class="built_in">varchar</span>(<span class="number">100</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">default</span> <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">  author <span class="built_in">varchar</span>(<span class="number">40</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">default</span> <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">  submission_date <span class="built_in">date</span> <span class="keyword">default</span> <span class="literal">NULL</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span>  (<span class="keyword">id</span>),</span><br><span class="line">  <span class="keyword">UNIQUE</span> <span class="keyword">KEY</span> AUTHOR_INDEX (author)</span><br><span class="line">) <span class="keyword">TYPE</span>=MyISAM;</span><br><span class="line"></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"><span class="comment">-- Dumping data for table `tbl`</span></span><br><span class="line"><span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> tbl </span><br><span class="line">       <span class="keyword">VALUES</span> (<span class="number">1</span>,<span class="string">&#x27;Learn PHP&#x27;</span>,<span class="string">&#x27;John Poul&#x27;</span>,<span class="string">&#x27;2007-05-24&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> tbl </span><br><span class="line">       <span class="keyword">VALUES</span> (<span class="number">2</span>,<span class="string">&#x27;Learn MySQL&#x27;</span>,<span class="string">&#x27;Abdul S&#x27;</span>,<span class="string">&#x27;2007-05-24&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> tbl </span><br><span class="line">       <span class="keyword">VALUES</span> (<span class="number">3</span>,<span class="string">&#x27;JAVA Tutorial&#x27;</span>,<span class="string">&#x27;Sanjay&#x27;</span>,<span class="string">&#x27;2007-05-06&#x27;</span>);</span><br></pre></td></tr></table></figure></p><p>如果你需要导出整个数据库的数据，可以使用以下命令：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysqldump -u root -p TestDB &gt; database_dump.txt</span></span><br><span class="line">password ******</span><br></pre></td></tr></table></figure></p><p>如果需要备份所有数据库，可以使用以下命令：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysqldump -u root -p --all-databases &gt; database_dump.txt</span></span><br><span class="line">password ******</span><br></pre></td></tr></table></figure></p><p><code>--all-databases</code> 选项在 MySQL 3.23.12 及以后版本加入。</p><p>该方法可用于实现数据库的备份策略。</p><h3 id="将数据表及数据库拷贝至其他主机"><a href="#将数据表及数据库拷贝至其他主机" class="headerlink" title="将数据表及数据库拷贝至其他主机"></a>将数据表及数据库拷贝至其他主机</h3><p>如果你需要将数据拷贝至其他的 MySQL 服务器上, 你可以在 <code>mysqldump</code> 命令中指定数据库名及数据表。</p><p>在源主机上执行以下命令，将数据备份至 <code>dump.txt</code> 文件中:</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysqldump -u root -p database_name table_name &gt; dump.txt</span></span><br><span class="line">password *****</span><br></pre></td></tr></table></figure></p><p>如果完整备份数据库，则无需使用特定的表名称。</p><p>如果你需要将备份的数据库导入到 MySQL 服务器中，可以使用以下命令，使用以下命令你需要确认数据库已经创建：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysql -u root -p database_name &lt; dump.txt</span></span><br><span class="line">password *****</span><br></pre></td></tr></table></figure></p><p>你也可以使用以下命令将导出的数据直接导入到远程的服务器上，但请确保两台服务器是相通的，是可以相互访问的：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysqldump -u root -p database_name \</span></span><br><span class="line"><span class="bash">       | mysql -h other-host.com database_name</span></span><br></pre></td></tr></table></figure></p><p>以上命令中使用了管道来将导出的数据导入到指定的远程主机上。</p><h2 id="导入数据"><a href="#导入数据" class="headerlink" title="导入数据"></a>导入数据</h2><h3 id="mysql-命令导入"><a href="#mysql-命令导入" class="headerlink" title="mysql 命令导入"></a>mysql 命令导入</h3><p>使用 <code>mysql</code> 命令导入语法格式为：</p><p><code>mysql -u 用户名 -p 密码 &lt; 要导入的数据库数据(TestDB.sql)</code></p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysql -uroot -p123456 &lt; TestDB.sql</span></span><br></pre></td></tr></table></figure></p><p>以上命令将将备份的整个数据库 <code>TestDB.sql</code> 导入。</p><h3 id="source-命令导入"><a href="#source-命令导入" class="headerlink" title="source 命令导入"></a>source 命令导入</h3><p><code>source</code> 命令导入数据库需要先登录到数库终端：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE DATABASE abc;      # 创建数据库</span><br><span class="line">mysql&gt; USE abc;                  # 使用已创建的数据库 </span><br><span class="line">mysql&gt; SET names utf8;           # 设置编码</span><br><span class="line">mysql&gt; source /home/abc/abc.sql  # 导入备份数据库</span><br></pre></td></tr></table></figure></p><h3 id="使用-LOAD-DATA-导入数据"><a href="#使用-LOAD-DATA-导入数据" class="headerlink" title="使用 LOAD DATA 导入数据"></a>使用 LOAD DATA 导入数据</h3><p>MySQL 中提供了<code>LOAD DATA INFILE</code> 语句来插入数据。 以下实例中将从当前目录中读取文件 <code>dump.txt</code>，将该文件中的数据插入到当前数据库的 <code>mytbl</code> 表中。</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; LOAD DATA LOCAL INFILE &#x27;dump.txt&#x27; INTO TABLE mytbl;</span><br></pre></td></tr></table></figure></p><p>如果指定 <code>LOCAL</code> 关键词，则表明从客户主机上按路径读取文件。如果没有指定，则文件在服务器上按路径读取文件。</p><p>你能明确地在 <code>LOAD DATA</code> 语句中指出列值的分隔符和行尾标记，但是默认标记是定位符和换行符。</p><p>两个命令的 <code>FIELDS</code> 和 <code>LINES</code> 子句的语法是一样的。两个子句都是可选的，但是如果两个同时被指定，<code>FIELDS</code> 子句必须出现在 <code>LINES</code> 子句之前。</p><p>如果用户指定一个 <code>FIELDS</code> 子句，它的子句（<code>TERMINATED BY</code>、<code>[OPTIONALLY] ENCLOSED BY</code> 和 <code>ESCAPED BY</code>）也是可选的，不过，用户必须至少指定它们中的一个。</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; LOAD DATA LOCAL INFILE &#x27;dump.txt&#x27; INTO TABLE mytbl</span><br><span class="line">    -&gt; FIELDS TERMINATED BY &#x27;:&#x27;</span><br><span class="line">    -&gt; LINES TERMINATED BY &#x27;\r\n&#x27;;</span><br></pre></td></tr></table></figure></p><p><code>LOAD DATA</code> 默认情况下是按照数据文件中列的顺序插入数据的，如果数据文件中的列与插入表中的列不一致，则需要指定列的顺序。</p><p>如，在数据文件中的列顺序是 <code>a, b, c</code>，但在插入表的列顺序为 <code>b, c, a</code>，则数据导入语法如下：</p><p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; LOAD DATA LOCAL INFILE &#x27;dump.txt&#x27; </span><br><span class="line">    -&gt; INTO TABLE mytbl (b, c, a);</span><br></pre></td></tr></table></figure></p><h3 id="使用-mysqlimport-导入数据"><a href="#使用-mysqlimport-导入数据" class="headerlink" title="使用 mysqlimport 导入数据"></a>使用 mysqlimport 导入数据</h3><p><code>mysqlimport</code> 客户端提供了 <code>LOAD DATA INFILEQL</code> 语句的一个命令行接口。<code>mysqlimport</code> 的大多数选项直接对应 <code>LOAD DATA INFILE</code> 子句。</p><p>从文件 <code>dump.txt</code> 中将数据导入到 <code>mytbl</code> 数据表中, 可以使用以下命令：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysqlimport -u root -p --<span class="built_in">local</span> mytbl dump.txt</span></span><br><span class="line">password *****</span><br></pre></td></tr></table></figure></p><p><code>mysqlimport</code> 命令可以指定选项来设置指定格式,命令语句格式如下：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysqlimport -u root -p --<span class="built_in">local</span> --fields-terminated-by=<span class="string">&quot;:&quot;</span> \</span></span><br><span class="line"><span class="bash">   --lines-terminated-by=<span class="string">&quot;\r\n&quot;</span>  mytbl dump.txt</span></span><br><span class="line">password *****</span><br></pre></td></tr></table></figure></p><p><code>mysqlimport</code> 语句中使用 <code>--columns</code> 选项来设置列的顺序：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> mysqlimport -u root -p --<span class="built_in">local</span> --columns=b,c,a \</span></span><br><span class="line"><span class="bash">    mytbl dump.txt</span></span><br><span class="line">password *****</span><br></pre></td></tr></table></figure></p><p><strong>mysqlimport 的常用选项介绍</strong></p><div class="table-container"><table><thead><tr><th style="text-align:center">选项</th><th style="text-align:center">功能</th></tr></thead><tbody><tr><td style="text-align:center">-d or --delete</td><td style="text-align:center">新数据导入数据表中之前删除数据数据表中的所有信息</td></tr><tr><td style="text-align:center">-f or --force</td><td style="text-align:center">不管是否遇到错误，mysqlimport 将强制继续插入数据</td></tr><tr><td style="text-align:center">-i or --ignore</td><td style="text-align:center">mysqlimport 跳过或者忽略那些有相同唯一关键字的行，<br>导入文件中的数据将被忽略。</td></tr><tr><td style="text-align:center">-l or -lock-tables</td><td style="text-align:center">数据被插入之前锁住表，这样就防止了在更新数据库时，<br>用户的查询和更新受到影响。</td></tr><tr><td style="text-align:center">-r or -replace</td><td style="text-align:center">这个选项与 -i 选项的作用相反；<br>此选项将替代表中有相同唯一关键字的记录。</td></tr><tr><td style="text-align:center">--fields -enclosed -by=char</td><td style="text-align:center">指定文本文件中数据的记录时以什么括起的，<br>很多情况下数据以双引号括起。<br>默认的情况下数据是没有被字符括起的。</td></tr><tr><td style="text-align:center">--fields -terminated -by=char</td><td style="text-align:center">指定各个数据的值之间的分隔符，在句号分隔的文件中，分隔符是句号。<br>可以用此选项指定数据之间的分隔符。默认的分隔符是跳格符（Tab）</td></tr><tr><td style="text-align:center">--lines -terminated -by=str</td><td style="text-align:center">此选项指定文本文件中行与行之间数据的分隔字符串或者字符。<br>默认的情况下 mysqlimport 以 newline 为行分隔符。<br>您可以选择用一个字符串来替代一个单个的字符： 一个新行或者一个回车。</td></tr></tbody></table></div><p><code>mysqlimport</code> 命令常用的选项还有 <code>-v</code> 显示版本（version），<code>-p</code> 提示输入密码（password）等。</p><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><h3 id="字符串函数"><a href="#字符串函数" class="headerlink" title="字符串函数"></a>字符串函数</h3><div class="table-container"><table><thead><tr><th style="text-align:center">函数</th><th style="text-align:center">描述</th><th style="text-align:center">实例</th></tr></thead><tbody><tr><td style="text-align:center">ASCII(s)</td><td style="text-align:center">返回字符串 s 的第一个字符的 ASCII 码。</td><td style="text-align:center">返回 CustomerName 字段第一个字母的 ASCII 码： <br> <code>SELECT ASCII(CustomerName) AS NumCodeOfFirstChar FROM Customers;</code></td></tr><tr><td style="text-align:center">CHAR_LENGTH(s)</td><td style="text-align:center">返回字符串 s 的字符数</td><td style="text-align:center">返回字符串 GOOGLE 的字符数： <br> <code>SELECT CHAR_LENGTH(&quot;GOOGLE&quot;) AS LengthOfString;</code></td></tr><tr><td style="text-align:center">CHARACTER_LENGTH(s)</td><td style="text-align:center">返回字符串 s 的字符数</td><td style="text-align:center">返回字符串 GOOGLE 的字符数： <br> <code>SELECT CHARACTER_LENGTH(&quot;GOOGLE&quot;) AS LengthOfString;</code></td></tr><tr><td style="text-align:center">CONCAT(s1, s2 ... sn)</td><td style="text-align:center">字符串 s1, s2 等多个字符串合并为一个字符串</td><td style="text-align:center">合并多个字符串： <br> <code>SELECT CONCAT(&quot;SQL &quot;, &quot;Baidu &quot;, &quot;Google &quot;, &quot;Facebook&quot;) AS ConcatenatedString;</code></td></tr><tr><td style="text-align:center">CONCAT_WS(x, s1, s2 ... sn)</td><td style="text-align:center">同 CONCAT(s1, s2, ...) 函数，但是每个字符串之间要加上 x，x 可以是分隔符</td><td style="text-align:center">合并多个字符串，并添加分隔符 s： <br> <code>SELECT CONCAT_WS(&quot;-&quot;, &quot;SQL&quot;, &quot;Tutorial&quot;, &quot;is&quot;, &quot;fun!&quot;)AS ConcatenatedString;</code></td></tr><tr><td style="text-align:center">FIELD(s, s1, s2 ...)</td><td style="text-align:center">返回第一个字符串 s 在字符串列表(s1, s2 ...)中的位置</td><td style="text-align:center">返回字符串 c 在列表值中的位置： <br> <code>SELECT FIELD(&quot;c&quot;, &quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;);  -- 3</code></td></tr><tr><td style="text-align:center">FIND_IN_SET(s1, s2)</td><td style="text-align:center">返回在字符串 s2 中与 s1 匹配的字符串的位置</td><td style="text-align:center">返回字符串 c 在指定字符串中的位置： <br> <code>SELECT FIND_IN_SET(&quot;c&quot;, &quot;a,b,c,d,e&quot;);  -- 5</code></td></tr><tr><td style="text-align:center">FORMAT(x, n)</td><td style="text-align:center">函数可以将数字 x 进行格式化 &quot;#,###.##&quot;, 将 x 保留到小数点后 n 位，最后一位四舍五入。</td><td style="text-align:center">格式化数字 &quot;#,###.##&quot; 形式： <br> <code>SELECT FORMAT(250500.5634, 2);  -- 250,500.56</code></td></tr><tr><td style="text-align:center">INSERT(s1, x, len, s2)</td><td style="text-align:center">字符串 s2 替换 s1 的 x 位置开始长度为 len 的字符串</td><td style="text-align:center">从字符串第一个位置开始的 6 个字符替换为 baidu： <br> <code>SELECT INSERT(&quot;google.com&quot;, 1, 6, &quot;baidu&quot;);  -- baidu.com</code></td></tr><tr><td style="text-align:center">LOCATE(s1, s)</td><td style="text-align:center">从字符串 s 中获取 s1 的开始位置</td><td style="text-align:center">获取 b 在字符串 abc 中的位置： <br> <code>SELECT LOCATE(&#39;st&#39;, &#39;myteststring&#39;);  -- 5</code> <br> 返回字符串 abc 中 b 的位置： <br> <code>SELECT LOCATE(&#39;b&#39;, &#39;abc&#39;);  -- 2</code></td></tr><tr><td style="text-align:center">LCASE(s)</td><td style="text-align:center">将字符串 s 的所有字母变成小写字母</td><td style="text-align:center">字符串 GOOGLE 转换为小写： <br> <code>SELECT LCASE(&#39;GOOGLE&#39;);  -- google</code></td></tr><tr><td style="text-align:center">LEFT(s, n)</td><td style="text-align:center">返回字符串 s 的前 n 个字符</td><td style="text-align:center">返回字符串 GOOGLE 中的前两个字符： <br> <code>SELECT LEFT(&#39;GOOGLE&#39;, 2);  -- GO</code></td></tr><tr><td style="text-align:center">LOWER(s)</td><td style="text-align:center">将字符串 s 的所有字母变成小写字母：</td><td style="text-align:center">字符串 GOOGLE 转换为小写： <br> <code>SELECT LOWER(&#39;GOOGLE&#39;);  -- google</code></td></tr><tr><td style="text-align:center">LPAD(s1, len, s2)</td><td style="text-align:center">在字符串 s1 的开始处填充字符串 s2，使字符串长度达到 len</td><td style="text-align:center">将字符串 xx 填充到 abc 字符串的开始处： <br> <code>SELECT LPAD(&#39;abc&#39;, 5,&#39;xx&#39;);  -- xxabc</code></td></tr><tr><td style="text-align:center">LTRIM(s)</td><td style="text-align:center">去掉字符串 s 开始处的空格</td><td style="text-align:center">去掉字符串 GOOGLE 开始处的空格： <br> <code>SELECT LTRIM(&quot;    GOOGLE&quot;) AS LeftTrimmedString;-- GOOGLE</code></td></tr><tr><td style="text-align:center">MID(s, n, len)</td><td style="text-align:center">从字符串 s 的 n 位置截取长度为 len 的子字符串，同 SUBSTRING(s, n, len)</td><td style="text-align:center">从字符串 GOOGLE 中的第 2 个位置截取 3 个 字符： <br> <code>SELECT MID(&quot;GOOGLE&quot;, 2, 3) AS ExtractString;  -- OOG</code></td></tr><tr><td style="text-align:center">POSITION(s1 IN s)</td><td style="text-align:center">从字符串 s 中获取 s1 的开始位置</td><td style="text-align:center">返回字符串 abc 中 b 的位置： <br> <code>SELECT POSITION(&#39;b&#39; in &#39;abc&#39;);  -- 2</code></td></tr><tr><td style="text-align:center">REPEAT(s, n)</td><td style="text-align:center">将字符串 s 重复 n 次</td><td style="text-align:center">将字符串 GOOGLE 重复三次： <br> <code>SELECT REPEAT(&#39;GOOGLE&#39;, 3);  -- GOOGLEGOOGLEGOOGLE</code></td></tr><tr><td style="text-align:center">REPLACE(s, s1, s2)</td><td style="text-align:center">将字符串 s2 替代字符串 s 中的字符串 s1</td><td style="text-align:center">将字符串 abc 中的字符 a 替换为字符 x： <br> <code>SELECT REPLACE(&#39;abc&#39;,&#39;a&#39;,&#39;x&#39;);  -- xbc</code></td></tr><tr><td style="text-align:center">REVERSE(s)</td><td style="text-align:center">将字符串 s 的顺序反过来</td><td style="text-align:center">将字符串 abc 的顺序反过来： <br> <code>SELECT REVERSE(&#39;abc&#39;);  -- cba</code></td></tr><tr><td style="text-align:center">RIGHT(s, n)</td><td style="text-align:center">返回字符串 s 的后 n 个字符</td><td style="text-align:center">返回字符串 GOOGLE 的后两个字符： <br> <code>SELECT RIGHT(&#39;GOOGLE&#39;, 2);  -- LE</code></td></tr><tr><td style="text-align:center">RPAD(s1, len, s2)</td><td style="text-align:center">在字符串 s1 的结尾处添加字符串 s2，使字符串的长度达到 len</td><td style="text-align:center">将字符串 xx 填充到 abc 字符串的结尾处： <br> <code>SELECT RPAD(&#39;abc&#39;, 5,&#39;xx&#39;);  -- abcxx</code></td></tr><tr><td style="text-align:center">RTRIM(s)</td><td style="text-align:center">去掉字符串 s 结尾处的空格</td><td style="text-align:center">去掉字符串 GOOGLE 的末尾空格： <br> <code>SELECT RTRIM(&quot;GOOGLE     &quot;) AS RightTrimmedString;  -- GOOGLE</code></td></tr><tr><td style="text-align:center">SPACE(n)</td><td style="text-align:center">返回 n 个空格</td><td style="text-align:center">返回 10 个空格： <br> <code>SELECT SPACE(10);</code></td></tr><tr><td style="text-align:center">STRCMP(s1, s2)</td><td style="text-align:center">比较字符串 s1 和 s2，如果 s1 与 s2 相等返回 0 ，如果 s1&gt;s2 返回 1，如果 s1&lt;s2 返回 -1</td><td style="text-align:center">比较字符串： <br> <code>SELECT STRCMP(&quot;GOOGLE&quot;, &quot;GOOGLE&quot;);  -- 0</code></td></tr><tr><td style="text-align:center">SUBSTR(s, start, length)</td><td style="text-align:center">从字符串 s 的 start 位置截取长度为 length 的子字符串</td><td style="text-align:center">从字符串 GOOGLE 中的第 2 个位置截取 3 个 字符： <br> <code>SELECT SUBSTR(&quot;GOOGLE&quot;, 2, 3) AS ExtractString;  -- OOG</code></td></tr><tr><td style="text-align:center">SUBSTRING(s, start, length)</td><td style="text-align:center">从字符串 s 的 start 位置截取长度为 length 的子字符串</td><td style="text-align:center">从字符串 GOOGLE 中的第 2 个位置截取 3 个字符： <br> <code>SELECT SUBSTRING(&quot;GOOGLE&quot;, 2, 3) AS ExtractString;  -- OOG</code></td></tr><tr><td style="text-align:center">SUBSTRING_INDEX(s, delimiter, number)</td><td style="text-align:center">返回从字符串 s 的第 number 个出现的分隔符 delimiter 之后的子串。 <br> 如果 number 是正数，返回第 number 个字符左边的字符串。 <br> 如果 number 是负数，返回第(number 的绝对值(从右边数))个字符右边的字符串。</td><td style="text-align:center"><code>SELECT SUBSTRING_INDEX(&#39;a*b&#39;, &#39;*&#39;, 1);  -- a</code> <br> <code>SELECT SUBSTRING_INDEX(&#39;a*b&#39;, &#39;*&#39;, -1);  -- b</code> <br> <code>SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(&#39;a*b*c*d*e&#39;, &#39;*&#39;, 3), &#39;*&#39;, -1);  -- c</code></td></tr><tr><td style="text-align:center">TRIM(s)</td><td style="text-align:center">去掉字符串 s 开始和结尾处的空格</td><td style="text-align:center">去掉字符串 GOOGLE 的首尾空格： <br> <code>SELECT TRIM(&#39;    GOOGLE    &#39;) AS TrimmedString;</code></td></tr><tr><td style="text-align:center">UCASE(s)</td><td style="text-align:center">将字符串转换为大写</td><td style="text-align:center">将字符串 google 转换为大写： <br> <code>SELECT UCASE(&quot;google&quot;);  -- GOOGLE</code></td></tr><tr><td style="text-align:center">UPPER(s)</td><td style="text-align:center">将字符串转换为大写</td><td style="text-align:center">将字符串 google 转换为大写： <br> <code>SELECT UPPER(&quot;google&quot;);  -- GOOGLE</code></td></tr></tbody></table></div><h3 id="数字函数"><a href="#数字函数" class="headerlink" title="数字函数"></a>数字函数</h3><div class="table-container"><table><thead><tr><th style="text-align:center">函数名</th><th style="text-align:center">描述</th><th style="text-align:center">实例</th></tr></thead><tbody><tr><td style="text-align:center">ABS(x)</td><td style="text-align:center">返回 x 的绝对值</td><td style="text-align:center"><code>SELECT ABS(-1);  -- 返回 1</code></td></tr><tr><td style="text-align:center">ACOS(x)</td><td style="text-align:center">求 x 的反余弦值（参数是弧度）</td><td style="text-align:center"><code>SELECT ACOS(0.25);</code></td></tr><tr><td style="text-align:center">ASIN(x)</td><td style="text-align:center">求反正弦值（参数是弧度）</td><td style="text-align:center"><code>SELECT ASIN(0.25);</code></td></tr><tr><td style="text-align:center">ATAN(x)</td><td style="text-align:center">求反正切值（参数是弧度）</td><td style="text-align:center"><code>SELECT ATAN(2.5);</code></td></tr><tr><td style="text-align:center">ATAN2(n, m)</td><td style="text-align:center">求反正切值（参数是弧度）</td><td style="text-align:center"><code>SELECT ATAN2(-0.8, 2);</code></td></tr><tr><td style="text-align:center">AVG(expression)</td><td style="text-align:center">返回一个表达式的平均值，expression 是一个字段</td><td style="text-align:center">返回 Products 表中 Price 字段的平均值： <br> <code>SELECT AVG(Price) AS AveragePrice FROM Products;</code></td></tr><tr><td style="text-align:center">CEIL(x)</td><td style="text-align:center">返回大于或等于 x 的最小整数</td><td style="text-align:center"><code>SELECT CEIL(1.5);  -- 返回 2</code></td></tr><tr><td style="text-align:center">CEILING(x)</td><td style="text-align:center">返回大于或等于 x 的最小整数</td><td style="text-align:center"><code>SELECT CEILING(1.5);  -- 返回 2</code></td></tr><tr><td style="text-align:center">COS(x)</td><td style="text-align:center">求余弦值（参数是弧度）</td><td style="text-align:center"><code>SELECT COS(2);</code></td></tr><tr><td style="text-align:center">COT(x)</td><td style="text-align:center">求余切值（参数是弧度）</td><td style="text-align:center"><code>SELECT COT(6);</code></td></tr><tr><td style="text-align:center">COUNT(expression)</td><td style="text-align:center">返回查询的记录总数，expression 参数是一个字段或者 * 号</td><td style="text-align:center">返回 Products 表中 products 字段总共有多少条记录： <br> <code>SELECT COUNT(ProductID) AS NumberOfProducts FROM Products;</code></td></tr><tr><td style="text-align:center">DEGREES(x)</td><td style="text-align:center">将弧度转换为角度</td><td style="text-align:center"><code>SELECT DEGREES(3.1415926535898);  -- 180</code></td></tr><tr><td style="text-align:center">n DIV m</td><td style="text-align:center">整除，n 为被除数，m 为除数</td><td style="text-align:center">计算 10 除于 5： <br> <code>SELECT 10 DIV 5;  -- 2</code></td></tr><tr><td style="text-align:center">EXP(x)</td><td style="text-align:center">返回 e 的 x 次方</td><td style="text-align:center">计算 e 的三次方： <br> <code>SELECT EXP(3);  -- 20.085536923188</code></td></tr><tr><td style="text-align:center">FLOOR(x)</td><td style="text-align:center">返回小于或等于 x 的最大整数</td><td style="text-align:center">小于或等于 1.5 的整数： <br> <code>SELECT FLOOR(1.5);  -- 返回 1</code></td></tr><tr><td style="text-align:center">GREATEST(expr1, expr2, expr3, ...)</td><td style="text-align:center">返回列表中的最大值</td><td style="text-align:center">返回以下数字列表中的最大值： <br> <code>SELECT GREATEST(3, 12, 34, 8, 25);  -- 34</code> <br> 返回以下字符串列表中的最大值： <br> <code>SELECT GREATEST(&quot;Google&quot;, &quot;Baidu&quot;, &quot;Apple&quot;);  -- Google</code></td></tr><tr><td style="text-align:center">LEAST(expr1, expr2, expr3, ...)</td><td style="text-align:center">返回列表中的最小值</td><td style="text-align:center">返回以下数字列表中的最小值： <br> <code>SELECT LEAST(3, 12, 34, 8, 25);  -- 3</code> <br> 返回以下字符串列表中的最小值： <br> <code>SELECT LEAST(&quot;Google&quot;, &quot;Baidu&quot;, &quot;Apple&quot;);  -- Apple</code></td></tr><tr><td style="text-align:center">LN(x)</td><td style="text-align:center">返回数字 x 的自然对数，以 e 为底。</td><td style="text-align:center">返回 2 的自然对数： <br> <code>SELECT LN(2);  -- 0.6931471805599453</code></td></tr><tr><td style="text-align:center">LOG(x) 或 LOG(base, x)</td><td style="text-align:center">返回自然对数（以 e 为底的对数）， <br> 如果带有 base 参数，则 base 为指定带底数。</td><td style="text-align:center"><code>SELECT LOG(20.085536923188);  -- 3</code> <br> <code>SELECT LOG(2, 4);  -- 2</code></td></tr><tr><td style="text-align:center">LOG10(x)</td><td style="text-align:center">返回以 10 为底的对数</td><td style="text-align:center"><code>SELECT LOG10(100);  -- 2</code></td></tr><tr><td style="text-align:center">LOG2(x)</td><td style="text-align:center">返回以 2 为底的对数</td><td style="text-align:center">返回以 2 为底 6 的对数： <br> <code>SELECT LOG2(6);  -- 2.584962500721156</code></td></tr><tr><td style="text-align:center">MAX(expression)</td><td style="text-align:center">返回字段 expression 中的最大值</td><td style="text-align:center">返回数据表 Products 中字段 Price 的最大值： <br> <code>SELECT MAX(Price) AS LargestPrice FROM Products;</code></td></tr><tr><td style="text-align:center">MIN(expression)</td><td style="text-align:center">返回字段 expression 中的最小值</td><td style="text-align:center">返回数据表 Products 中字段 Price 的最小值： <br> <code>SELECT MIN(Price) AS MinPrice FROM Products;</code></td></tr><tr><td style="text-align:center">MOD(x,y)</td><td style="text-align:center">返回 x 除以 y 以后的余数</td><td style="text-align:center">5 除于 2 的余数： <br> <code>SELECT MOD(5, 2);  -- 1</code></td></tr><tr><td style="text-align:center">PI()</td><td style="text-align:center">返回圆周率(3.141593）</td><td style="text-align:center"><code>SELECT PI();  --3.141593</code></td></tr><tr><td style="text-align:center">POW(x,y)</td><td style="text-align:center">返回 x 的 y 次方</td><td style="text-align:center">2 的 3 次方： <br> <code>SELECT POW(2, 3);  -- 8</code></td></tr><tr><td style="text-align:center">POWER(x,y)</td><td style="text-align:center">返回 x 的 y 次方</td><td style="text-align:center">2 的 3 次方： <br> <code>SELECT POWER(2, 3);  -- 8</code></td></tr><tr><td style="text-align:center">RADIANS(x)</td><td style="text-align:center">将角度转换为弧度</td><td style="text-align:center">180 度转换为弧度： <br> <code>SELECT RADIANS(180);  -- 3.1415926535898</code></td></tr><tr><td style="text-align:center">RAND()</td><td style="text-align:center">返回 0 到 1 的随机数</td><td style="text-align:center"><code>SELECT RAND();  --0.93099315644334</code></td></tr><tr><td style="text-align:center">ROUND(x)</td><td style="text-align:center">返回离 x 最近的整数,四舍五入</td><td style="text-align:center"><code>SELECT ROUND(1.23456);  --1</code></td></tr><tr><td style="text-align:center">SIGN(x)</td><td style="text-align:center">返回 x 的符号，x 是负数、0、 正数分别返回 -1、0 和 1</td><td style="text-align:center"><code>SELECT SIGN(-10);  -- (-1)</code></td></tr><tr><td style="text-align:center">SIN(x)</td><td style="text-align:center">求正弦值（参数是弧度）</td><td style="text-align:center"><code>SELECT SIN(RADIANS(30));  -- 0.5</code></td></tr><tr><td style="text-align:center">SQRT(x)</td><td style="text-align:center">返回 x 的平方根</td><td style="text-align:center">25 的平方根： <br> <code>SELECT SQRT(25);  -- 5</code></td></tr><tr><td style="text-align:center">SUM(expression)</td><td style="text-align:center">返回指定字段的总和</td><td style="text-align:center">计算 OrderDetails 表中字段 Quantity 的总和： <br> <code>SELECT SUM(Quantity) AS TotalItemsOrdered FROM OrderDetails;</code></td></tr><tr><td style="text-align:center">TAN(x)</td><td style="text-align:center">求正切值（参数是弧度）</td><td style="text-align:center"><code>SELECT TAN(1.75);  -- (-5.52037992250933)</code></td></tr><tr><td style="text-align:center">TRUNCATE(x, y)</td><td style="text-align:center">返回数值 x 保留到小数点后 y 位的值（与 ROUND 最大的区别是不会进行四舍五入）</td><td style="text-align:center"><code>SELECT TRUNCATE(1.23456, 3);  -- 1.234</code></td></tr></tbody></table></div><h3 id="日期函数"><a href="#日期函数" class="headerlink" title="日期函数"></a>日期函数</h3><div class="table-container"><table><thead><tr><th style="text-align:center">函数名</th><th style="text-align:center">描述</th><th style="text-align:center">实例</th></tr></thead><tbody><tr><td style="text-align:center">ADDDATE(d, n)</td><td style="text-align:center">计算起始日期 d 加上 n 天的日期</td><td style="text-align:center"><code>SELECT ADDDATE(&quot;2017-06-15&quot;, INTERVAL 10 DAY);  --2017-06-25</code></td></tr><tr><td style="text-align:center">ADDTIME(t, n)</td><td style="text-align:center">n 是一个时间表达式，时间 t 加上时间表达式 n</td><td style="text-align:center">加 5 秒： <br> <code>SELECT ADDTIME(&#39;2011-11-11 11:11:11&#39;, 5);  --2011-11-11 11:11:16</code> <br> 添加 2 小时, 10 分钟, 5 秒： <br> <code>SELECT ADDTIME(&quot;2020-06-15 09:34:21&quot;, &quot;2:10:5&quot;);  -- 2020-06-15 11:44:26</code></td></tr><tr><td style="text-align:center">CURDATE()</td><td style="text-align:center">返回当前日期</td><td style="text-align:center"><code>SELECT CURDATE();  -- 2018-09-19</code></td></tr><tr><td style="text-align:center">CURRENT_DATE()</td><td style="text-align:center">返回当前日期</td><td style="text-align:center"><code>SELECT CURRENT_DATE();  -- 2018-09-19</code></td></tr><tr><td style="text-align:center">CURRENT_TIME</td><td style="text-align:center">返回当前时间</td><td style="text-align:center"><code>SELECT CURRENT_TIME();  -- 19:59:02</code></td></tr><tr><td style="text-align:center">CURRENT_TIMESTAMP()</td><td style="text-align:center">返回当前日期和时间</td><td style="text-align:center"><code>SELECT CURRENT_TIMESTAMP();  -- 2018-09-19 20:57:43</code></td></tr><tr><td style="text-align:center">CURTIME()</td><td style="text-align:center">返回当前时间</td><td style="text-align:center"><code>SELECT CURTIME();  -- 19:59:02</code></td></tr><tr><td style="text-align:center">DATE()</td><td style="text-align:center">从日期或日期时间表达式中提取日期值</td><td style="text-align:center"><code>SELECT DATE(&quot;2017-06-15&quot;);  -- 2017-06-15</code></td></tr><tr><td style="text-align:center">DATEDIFF(d1, d2)</td><td style="text-align:center">计算日期 d1 和 d2 之间相隔的天数</td><td style="text-align:center"><code>SELECT DATEDIFF(&#39;2001-01-01&#39;, &#39;2001-02-02&#39;);  -- (-32)</code></td></tr><tr><td style="text-align:center">DATE_ADD(d，INTERVAL expr type)</td><td style="text-align:center">计算起始日期 d 加上一个时间段后的日期</td><td style="text-align:center"><code>SELECT ADDDATE(&#39;2011-11-11 11:11:11&#39;, 1);  -- 2011-11-12 11:11:11（默认是天）</code> <br> <code>SELECT ADDDATE(&#39;2011-11-11 11:11:11&#39;, INTERVAL 5 MINUTE);  -- 2011-11-11 11:16:11 （TYPE 的取值与上面那个列出来的函数类似）</code></td></tr><tr><td style="text-align:center">DATE_FORMAT(d, f)</td><td style="text-align:center">按表达式 f 的要求显示日期 d</td><td style="text-align:center"><code>SELECT DATE_FORMAT(&#39;2011-11-11 11:11:11&#39;,&#39;%Y-%m-%d %r&#39;);  -- 2011-11-11 11:11:11 AM</code></td></tr><tr><td style="text-align:center">DATE_SUB(date, INTERVAL expr type)</td><td style="text-align:center">函数从日期减去指定的时间间隔。</td><td style="text-align:center">Orders 表中 OrderDate 字段减去 2 天： <br> <code>SELECT OrderId, DATE_SUB(OrderDate,INTERVAL 2 DAY) AS OrderPayDate FROM Orders;</code></td></tr><tr><td style="text-align:center">DAY(d)</td><td style="text-align:center">返回日期值 d 的日期部分</td><td style="text-align:center"><code>SELECT DAY(&quot;2017-06-15&quot;);  -- 15</code></td></tr><tr><td style="text-align:center">DAYNAME(d)</td><td style="text-align:center">返回日期 d 是星期几，如 Monday, Tuesday</td><td style="text-align:center"><code>SELECT DAYNAME(&#39;2011-11-11 11:11:11&#39;);  --Friday</code></td></tr><tr><td style="text-align:center">DAYOFMONTH(d)</td><td style="text-align:center">计算日期 d 是本月的第几天</td><td style="text-align:center"><code>SELECT DAYOFMONTH(&#39;2011-11-11 11:11:11&#39;);  --11</code></td></tr><tr><td style="text-align:center">DAYOFWEEK(d)</td><td style="text-align:center">日期 d 今天是星期几，1 星期日，2 星期一，以此类推</td><td style="text-align:center"><code>SELECT DAYOFWEEK(&#39;2011-11-11 11:11:11&#39;);  --6</code></td></tr><tr><td style="text-align:center">DAYOFYEAR(d)</td><td style="text-align:center">计算日期 d 是本年的第几天</td><td style="text-align:center"><code>SELECT DAYOFYEAR(&#39;2011-11-11 11:11:11&#39;);  --315</code></td></tr><tr><td style="text-align:center">EXTRACT(type FROM d)</td><td style="text-align:center">从日期 d 中获取指定的值，type 指定返回的值。 <br> type 可取值为： <br> MICROSECOND <br> SECOND <br> MINUTE <br> HOUR <br> DAY <br> WEEK <br> MONTH <br> QUARTER <br> YEAR <br> SECOND_MICROSECOND <br> MINUTE_MICROSECOND <br> MINUTE_SECOND <br> HOUR_MICROSECOND <br> HOUR_SECOND <br> HOUR_MINUTE <br> DAY_MICROSECOND <br> DAY_SECOND <br> DAY_MINUTE <br> DAY_HOUR <br> YEAR_MONTH</td><td style="text-align:center"><code>SELECT EXTRACT(MINUTE FROM &#39;2011-11-11 11:11:11&#39;);  -- 11</code></td></tr><tr><td style="text-align:center">FROM_DAYS(n)</td><td style="text-align:center">计算从 0000 年 1 月 1 日开始 n 天后的日期</td><td style="text-align:center"><code>SELECT FROM_DAYS(1111);  -- 0003-01-16</code></td></tr><tr><td style="text-align:center">HOUR(t)</td><td style="text-align:center">返回 t 中的小时值</td><td style="text-align:center"><code>SELECT HOUR(&#39;1:2:3&#39;);  -- 1</code></td></tr><tr><td style="text-align:center">LAST_DAY(d)</td><td style="text-align:center">返回给给定日期的那一月份的最后一天</td><td style="text-align:center"><code>SELECT LAST_DAY(&quot;2017-06-20&quot;);  -- 2017-06-30</code></td></tr><tr><td style="text-align:center">LOCALTIME()</td><td style="text-align:center">返回当前日期和时间</td><td style="text-align:center"><code>SELECT LOCALTIME();  -- 2018-09-19 20:57:43</code></td></tr><tr><td style="text-align:center">LOCALTIMESTAMP()</td><td style="text-align:center">返回当前日期和时间</td><td style="text-align:center"><code>SELECT LOCALTIMESTAMP();  -- 2018-09-19 20:57:43</code></td></tr><tr><td style="text-align:center">MAKEDATE(year, day-of-year)</td><td style="text-align:center">基于给定参数年份 year 和所在年中的天数序号 day-of-year 返回一个日期</td><td style="text-align:center"><code>SELECT MAKEDATE(2017, 3);  -- 2017-01-03</code></td></tr><tr><td style="text-align:center">MAKETIME(hour, minute, second)</td><td style="text-align:center">组合时间，参数分别为小时、分钟、秒</td><td style="text-align:center"><code>SELECT MAKETIME(11, 35, 4);  -- 11:35:04</code></td></tr><tr><td style="text-align:center">MICROSECOND(date)</td><td style="text-align:center">返回日期参数所对应的微秒数</td><td style="text-align:center"><code>SELECT MICROSECOND(&quot;2017-06-20 09:34:00.000023&quot;);  -- 23</code></td></tr><tr><td style="text-align:center">MINUTE(t)</td><td style="text-align:center">返回 t 中的分钟值</td><td style="text-align:center"><code>SELECT MINUTE(&#39;1:2:3&#39;);  -- 2</code></td></tr><tr><td style="text-align:center">MONTHNAME(d)</td><td style="text-align:center">返回日期当中的月份名称，如 November</td><td style="text-align:center"><code>SELECT MONTHNAME(&#39;2011-11-11 11:11:11&#39;);  -- November</code></td></tr><tr><td style="text-align:center">MONTH(d)</td><td style="text-align:center">返回日期 d 中的月份值，1 到 12</td><td style="text-align:center"><code>SELECT MONTH(&#39;2011-11-11 11:11:11&#39;);  --11</code></td></tr><tr><td style="text-align:center">NOW()</td><td style="text-align:center">返回当前日期和时间</td><td style="text-align:center"><code>SELECT NOW();  -- 2018-09-19 20:57:43</code></td></tr><tr><td style="text-align:center">PERIOD_ADD(period, number)</td><td style="text-align:center">为 年-月 组合日期添加一个时段</td><td style="text-align:center"><code>SELECT PERIOD_ADD(201703, 5);  -- 201708</code></td></tr><tr><td style="text-align:center">PERIOD_DIFF(period1, period2)</td><td style="text-align:center">返回两个时段之间的月份差值</td><td style="text-align:center"><code>SELECT PERIOD_DIFF(201710, 201703);  -- 7</code></td></tr><tr><td style="text-align:center">QUARTER(d)</td><td style="text-align:center">返回日期 d 是第几季节，返回 1 到 4</td><td style="text-align:center"><code>SELECT QUARTER(&#39;2011-11-11 11:11:11&#39;);  -- 4</code></td></tr><tr><td style="text-align:center">SECOND(t)</td><td style="text-align:center">返回 t 中的秒钟值</td><td style="text-align:center"><code>SELECT SECOND(&#39;1:2:3&#39;);  -- 3</code></td></tr><tr><td style="text-align:center">SEC_TO_TIME(s)</td><td style="text-align:center">将以秒为单位的时间 s 转换为时分秒的格式</td><td style="text-align:center"><code>SELECT SEC_TO_TIME(4320);  -- 01:12:00</code></td></tr><tr><td style="text-align:center">STR_TO_DATE(string, format_mask)</td><td style="text-align:center">将字符串转变为日期</td><td style="text-align:center"><code>SELECT STR_TO_DATE(&quot;August 10 2017&quot;, &quot;%M %d %Y&quot;);  -- 2017-08-10</code></td></tr><tr><td style="text-align:center">SUBDATE(d, n)</td><td style="text-align:center">日期 d 减去 n 天后的日期</td><td style="text-align:center"><code>SELECT SUBDATE(&#39;2011-11-11 11:11:11&#39;, 1);  --2011-11-10 11:11:11 (默认是天)</code></td></tr><tr><td style="text-align:center">SUBTIME(t, n)</td><td style="text-align:center">时间 t 减去 n 秒的时间</td><td style="text-align:center"><code>SELECT SUBTIME(&#39;2011-11-11 11:11:11&#39;, 5);  --2011-11-11 11:11:06 (秒)</code></td></tr><tr><td style="text-align:center">SYSDATE()</td><td style="text-align:center">返回当前日期和时间</td><td style="text-align:center"><code>SELECT SYSDATE();  -- 2018-09-19 20:57:43</code></td></tr><tr><td style="text-align:center">TIME(expression)</td><td style="text-align:center">提取传入表达式的时间部分</td><td style="text-align:center"><code>SELECT TIME(&quot;19:30:10&quot;);  -- 19:30:10</code></td></tr><tr><td style="text-align:center">TIME_FORMAT(t, f)</td><td style="text-align:center">按表达式 f 的要求显示时间 t</td><td style="text-align:center"><code>SELECT TIME_FORMAT(&#39;11:11:11&#39;, &#39;%r&#39;);  -- 11:11:11 AM</code></td></tr><tr><td style="text-align:center">TIME_TO_SEC(t)</td><td style="text-align:center">将时间 t 转换为秒</td><td style="text-align:center"><code>SELECT TIME_TO_SEC(&#39;1:12:00&#39;);  -- 4320</code></td></tr><tr><td style="text-align:center">TIMEDIFF(time1, time2)</td><td style="text-align:center">计算时间差值</td><td style="text-align:center"><code>SELECT TIMEDIFF(&quot;13:10:11&quot;, &quot;13:10:10&quot;);  -- 00:00:01</code></td></tr><tr><td style="text-align:center">TIMESTAMP(expression, interval)</td><td style="text-align:center">单个参数时，函数返回日期或日期时间表达式；有 2 个参数时，将参数加和</td><td style="text-align:center"><code>SELECT TIMESTAMP(&quot;2017-07-23&quot;, &quot;13:10:11&quot;);  -- 2017-07-23 13:10:11</code></td></tr><tr><td style="text-align:center">TO_DAYS(d)</td><td style="text-align:center">计算日期 d 距离 0000 年 1 月 1 日的天数</td><td style="text-align:center"><code>SELECT TO_DAYS(&#39;0001-01-01 01:01:01&#39;);  -- 366</code></td></tr><tr><td style="text-align:center">WEEK(d)</td><td style="text-align:center">计算日期 d 是本年的第几个星期，范围是 0 到 53</td><td style="text-align:center"><code>SELECT WEEK(&#39;2011-11-11 11:11:11&#39;);  -- 45</code></td></tr><tr><td style="text-align:center">WEEKDAY(d)</td><td style="text-align:center">日期 d 是星期几，0 表示星期一，1 表示星期二</td><td style="text-align:center"><code>SELECT WEEKDAY(&quot;2017-06-15&quot;);  -- 3</code></td></tr><tr><td style="text-align:center">WEEKOFYEAR(d)</td><td style="text-align:center">计算日期 d 是本年的第几个星期，范围是 0 到 53</td><td style="text-align:center"><code>SELECT WEEKOFYEAR(&#39;2011-11-11 11:11:11&#39;);  -- 45</code></td></tr><tr><td style="text-align:center">YEAR(d)</td><td style="text-align:center">返回年份</td><td style="text-align:center"><code>SELECT YEAR(&quot;2017-06-15&quot;);  -- 2017</code></td></tr><tr><td style="text-align:center">YEARWEEK(date, mode)</td><td style="text-align:center">返回年份及第几周（0 到 53），mode 中 0 表示周天，1 表示周一，以此类推</td><td style="text-align:center"><code>SELECT YEARWEEK(&quot;2017-06-15&quot;);  -- 201724</code></td></tr></tbody></table></div><h3 id="高级函数"><a href="#高级函数" class="headerlink" title="高级函数"></a>高级函数</h3><div class="table-container"><table><thead><tr><th style="text-align:center">函数名</th><th style="text-align:center">描述</th><th style="text-align:center">实例</th></tr></thead><tbody><tr><td style="text-align:center">BIN(x)</td><td style="text-align:center">返回 x 的二进制编码</td><td style="text-align:center">15 的 2 进制编码： <br> <code>SELECT BIN(15);  -- 1111</code></td></tr><tr><td style="text-align:center">BINARY(s)</td><td style="text-align:center">将字符串 s 转换为二进制字符串</td><td style="text-align:center"><code>SELECT BINARY &quot;GOOGLE&quot;;  -- GOOGLE</code></td></tr><tr><td style="text-align:center">CASE expression <br> WHEN condition1 THEN result1 <br> WHEN condition2 THEN result2 <br> ... <br> WHEN conditionN THEN resultN <br> ELSE result</td><td style="text-align:center">END &vert; CASE 表示函数开始，END 表示函数结束。 <br> 如果 condition1 成立，则返回 result1,  <br> 如果 condition2 成立，则返回 result2， <br> 当全部不成立则返回 result，而当有一个成立之后，后面的就不执行了。</td><td style="text-align:center"><code>SELECT CASE</code> <br> <code>WHEN 1 &gt; 0</code> <br> <code>THEN &#39;1 &gt; 0&#39;</code> <br> <code>WHEN 2 &gt; 0</code> <br> <code>THEN &#39;2 &gt; 0&#39;</code> <br> <code>ELSE &#39;3 &gt; 0&#39;</code> <br> <code>END</code> <br> <code>-- 1 &gt; 0</code></td></tr><tr><td style="text-align:center">CAST(x AS type)</td><td style="text-align:center">转换数据类型</td><td style="text-align:center">字符串日期转换为日期： <br> <code>SELECT CAST(&quot;2017-08-29&quot; AS DATE);  -- 2017-08-29</code></td></tr><tr><td style="text-align:center">COALESCE(expr1, expr2, ...., expr_n)</td><td style="text-align:center">返回参数中的第一个非空表达式（从左向右）</td><td style="text-align:center"><code>SELECT COALESCE(NULL, NULL, NULL, &#39;baidu.com&#39;, NULL, &#39;google.com&#39;);  -- baidu.com</code></td></tr><tr><td style="text-align:center">CONNECTION_ID()</td><td style="text-align:center">返回唯一的连接 ID</td><td style="text-align:center"><code>SELECT CONNECTION_ID();  -- 4292835</code></td></tr><tr><td style="text-align:center">CONV(x, f1, f2)</td><td style="text-align:center">返回 f1 进制数变成 f2 进制数</td><td style="text-align:center"><code>SELECT CONV(15, 10, 2);  -- 1111</code></td></tr><tr><td style="text-align:center">CONVERT(s USING cs)</td><td style="text-align:center">函数将字符串 s 的字符集变成 cs</td><td style="text-align:center"><code>SELECT CHARSET(&#39;ABC&#39;)  --utf-8</code> <br> <code>SELECT CHARSET(CONVERT(&#39;ABC&#39; USING gbk))  --gbk</code></td></tr><tr><td style="text-align:center">CURRENT_USER()</td><td style="text-align:center">返回当前用户</td><td style="text-align:center"><code>SELECT CURRENT_USER();  -- root@localhost</code></td></tr><tr><td style="text-align:center">DATABASE()</td><td style="text-align:center">返回当前数据库名</td><td style="text-align:center"><code>SELECT DATABASE();  -- testdb</code></td></tr><tr><td style="text-align:center">IF(expr, v1, v2)</td><td style="text-align:center">如果表达式 expr 成立，返回结果 v1；否则，返回结果 v2。</td><td style="text-align:center"><code>SELECT IF(1 &gt; 0,&#39;正确&#39;,&#39;错误&#39;)  --正确</code></td></tr><tr><td style="text-align:center">IFNULL(v1, v2)</td><td style="text-align:center">如果 v1 的值不为 NULL，则返回 v1，否则返回 v2。</td><td style="text-align:center"><code>SELECT IFNULL(NULL, &#39;Hello Word&#39;)  --Hello Word</code></td></tr><tr><td style="text-align:center">ISNULL(expression)</td><td style="text-align:center">判断表达式是否为 NULL</td><td style="text-align:center"><code>SELECT ISNULL(NULL);  --1</code></td></tr><tr><td style="text-align:center">LAST_INSERT_ID()</td><td style="text-align:center">返回最近生成的 AUTO_INCREMENT 值</td><td style="text-align:center"><code>SELECT LAST_INSERT_ID();  --6</code></td></tr><tr><td style="text-align:center">NULLIF(expr1, expr2)</td><td style="text-align:center">比较两个字符串，如果字符串 expr1 与 expr2 相等 返回 NULL，否则返回 expr1</td><td style="text-align:center"><code>SELECT NULLIF(25, 25);  --</code></td></tr><tr><td style="text-align:center">SESSION_USER()</td><td style="text-align:center">返回当前用户</td><td style="text-align:center"><code>SELECT SESSION_USER();  -- root@localhost</code></td></tr><tr><td style="text-align:center">SYSTEM_USER()</td><td style="text-align:center">返回当前用户</td><td style="text-align:center"><code>SELECT SYSTEM_USER();  -- root@localhost</code></td></tr><tr><td style="text-align:center">USER()</td><td style="text-align:center">返回当前用户</td><td style="text-align:center"><code>SELECT USER();  -- root@localhost</code></td></tr><tr><td style="text-align:center">VERSION()</td><td style="text-align:center">返回数据库的版本号</td><td style="text-align:center"><code>SELECT VERSION()  -- 5.6.34</code></td></tr></tbody></table></div><h2 id="运算符"><a href="#运算符" class="headerlink" title="运算符"></a>运算符</h2><p>本章节我们主要介绍 MySQL 的运算符及运算符的优先级。 MySQL 主要有以下几种运算符：</p><ul><li>算术运算符</li><li>比较运算符</li><li>逻辑运算符</li><li>位运算符</li></ul><h3 id="算术运算符"><a href="#算术运算符" class="headerlink" title="算术运算符"></a>算术运算符</h3><p>MySQL 支持的算术运算符包括:</p><div class="table-container"><table><thead><tr><th style="text-align:center">运算符</th><th style="text-align:center">作用</th></tr></thead><tbody><tr><td style="text-align:center">+</td><td style="text-align:center">加法</td></tr><tr><td style="text-align:center">-</td><td style="text-align:center">减法</td></tr><tr><td style="text-align:center">*</td><td style="text-align:center">乘法</td></tr><tr><td style="text-align:center">/ 或 DIV</td><td style="text-align:center">除法</td></tr><tr><td style="text-align:center">% 或 MOD</td><td style="text-align:center">取余</td></tr></tbody></table></div><p>在除法运算和模运算中，如果除数为 0，将是非法除数，返回结果为 NULL。</p><ol><li><p>加</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 1+2;</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">| 1+2 |</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">|   3 |</span><br><span class="line">+<span class="comment">-----+</span></span><br></pre></td></tr></table></figure></p></li><li><p>减</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 1-2;</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">| 1-2 |</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">|  -1 |</span><br><span class="line">+<span class="comment">-----+</span></span><br></pre></td></tr></table></figure></p></li><li><p>乘</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2*3;</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">| 2*3 |</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">|   6 |</span><br><span class="line">+<span class="comment">-----+</span></span><br></pre></td></tr></table></figure></p></li><li><p>除</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2/3;</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">| 2/3    |</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">| 0.6667 |</span><br><span class="line">+<span class="comment">--------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>商</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 10 DIV 4;</span><br><span class="line">+<span class="comment">----------+</span></span><br><span class="line">| 10 DIV 4 |</span><br><span class="line">+<span class="comment">----------+</span></span><br><span class="line">|        2 |</span><br><span class="line">+<span class="comment">----------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>取余</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 10 MOD 4;</span><br><span class="line">+<span class="comment">----------+</span></span><br><span class="line">| 10 MOD 4 |</span><br><span class="line">+<span class="comment">----------+</span></span><br><span class="line">|        2 |</span><br><span class="line">+<span class="comment">----------+</span></span><br></pre></td></tr></table></figure></p></li></ol><h3 id="比较运算符"><a href="#比较运算符" class="headerlink" title="比较运算符"></a>比较运算符</h3><p><code>SELECT</code> 语句中的条件语句经常要使用比较运算符。通过这些比较运算符，可以判断表中的哪些记录是符合条件的。比较结果为真，则返回 <code>1</code>，为假则返回 <code>0</code>，比较结果不确定则返回 <code>NULL</code>。</p><div class="table-container"><table><thead><tr><th style="text-align:center">符号</th><th style="text-align:center">描述</th><th style="text-align:center">备注</th></tr></thead><tbody><tr><td style="text-align:center">=</td><td style="text-align:center">等于</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">&lt;&gt;, !=</td><td style="text-align:center">不等于</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">&gt;</td><td style="text-align:center">大于</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">&lt;</td><td style="text-align:center">小于</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">&lt;=</td><td style="text-align:center">小于等于</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">&gt;=</td><td style="text-align:center">大于等于</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">BETWEEN</td><td style="text-align:center">在两值之间</td><td style="text-align:center">&gt;=min &amp;&amp; &lt;=max</td></tr><tr><td style="text-align:center">NOT BETWEEN</td><td style="text-align:center">不在两值之间</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">IN</td><td style="text-align:center">在集合中</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">NOT IN</td><td style="text-align:center">不在集合中</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">&lt;=&gt;</td><td style="text-align:center">严格比较两个 NULL 值是否相等</td><td style="text-align:center">两个操作码均为 NULL 时，其所得值为 1； <br> 而当一个操作码为 NULL 时，其所得值为 0</td></tr><tr><td style="text-align:center">LIKE</td><td style="text-align:center">模糊匹配</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">REGEXP 或 RLIKE</td><td style="text-align:center">正则式匹配</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">IS NULL</td><td style="text-align:center">为空</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">IS NOT NULL</td><td style="text-align:center">不为空</td><td style="text-align:center">&nbsp;</td></tr></tbody></table></div><ol><li><p>等于</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2=3;</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">| 2=3 |</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">|   0 |</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">mysql&gt; SELECT NULL = NULL;</span><br><span class="line">+<span class="comment">-------------+</span></span><br><span class="line">| NULL = NULL |</span><br><span class="line">+<span class="comment">-------------+</span></span><br><span class="line">|        NULL |</span><br><span class="line">+<span class="comment">-------------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>不等于</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2&lt;&gt;3;</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">| 2&lt;&gt;3 |</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">|    1 |</span><br><span class="line">+<span class="comment">------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>安全等于</p><p> 与 <code>=</code> 的区别在于当两个操作码均为 <code>NULL</code> 时，其所得值为 <code>1</code> 而不为 <code>NULL</code>，而当一个操作码为 <code>NULL</code> 时，其所得值为 <code>0</code> 而不为 <code>NULL</code>。</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2&lt;=&gt;3;</span><br><span class="line">+<span class="comment">-------+</span></span><br><span class="line">| 2&lt;=&gt;3 |</span><br><span class="line">+<span class="comment">-------+</span></span><br><span class="line">|     0 |</span><br><span class="line">+<span class="comment">-------+</span></span><br><span class="line">mysql&gt; SELECT NULL=NULL;</span><br><span class="line">+<span class="comment">-----------+</span></span><br><span class="line">| NULL=NULL |</span><br><span class="line">+<span class="comment">-----------+</span></span><br><span class="line">|      NULL |</span><br><span class="line">+<span class="comment">-----------+</span></span><br><span class="line">mysql&gt; SELECT NULL&lt;=&gt;NULL;</span><br><span class="line">+<span class="comment">-------------+</span></span><br><span class="line">| NULL&lt;=&gt;NULL |</span><br><span class="line">+<span class="comment">-------------+</span></span><br><span class="line">|           1 |</span><br><span class="line">+<span class="comment">-------------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>小于</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2&lt;3;</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">| 2&lt;3 |</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">|   1 |</span><br><span class="line">+<span class="comment">-----+</span></span><br></pre></td></tr></table></figure></p></li><li><p>小于等于</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2&lt;=3;</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">| 2&lt;=3 |</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">|    1 |</span><br><span class="line">+<span class="comment">------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>大于</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2&gt;3;</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">| 2&gt;3 |</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">|   0 |</span><br><span class="line">+<span class="comment">-----+</span></span><br></pre></td></tr></table></figure></p></li><li><p>大于等于</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2&gt;=3;</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">| 2&gt;=3 |</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">|    0 |</span><br><span class="line">+<span class="comment">------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>BETWEEN</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 5 BETWEEN 1 AND 10;</span><br><span class="line">+<span class="comment">--------------------+</span></span><br><span class="line">| 5 BETWEEN 1 AND 10 |</span><br><span class="line">+<span class="comment">--------------------+</span></span><br><span class="line">|                  1 |</span><br><span class="line">+<span class="comment">--------------------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>IN</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 5 IN (1, 2, 3, 4, 5);</span><br><span class="line">+<span class="comment">------------------+</span></span><br><span class="line">| 5 IN (1, 2, 3, 4, 5) |</span><br><span class="line">+<span class="comment">----------------------+</span></span><br><span class="line">|                    1 |</span><br><span class="line">+<span class="comment">----------------------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>NOT IN</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 5 NOT IN (1, 2, 3, 4, 5);</span><br><span class="line">+<span class="comment">--------------------------+</span></span><br><span class="line">| 5 NOT IN (1, 2, 3, 4, 5) |</span><br><span class="line">+<span class="comment">--------------------------+</span></span><br><span class="line">|                        0 |</span><br><span class="line">+<span class="comment">--------------------------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>IS NULL</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT NULL IS NULL;</span><br><span class="line">+<span class="comment">--------------+</span></span><br><span class="line">| NULL IS NULL |</span><br><span class="line">+<span class="comment">--------------+</span></span><br><span class="line">|            1 |</span><br><span class="line">+<span class="comment">--------------+</span></span><br><span class="line">mysql&gt; SELECT &#x27;a&#x27; IS NULL;</span><br><span class="line">+<span class="comment">-------------+</span></span><br><span class="line">| &#x27;a&#x27; IS NULL |</span><br><span class="line">+<span class="comment">-------------+</span></span><br><span class="line">|           0 |</span><br><span class="line">+<span class="comment">-------------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>IS NOT NULL</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT NULL IS NOT NULL;</span><br><span class="line">+<span class="comment">------------------+</span></span><br><span class="line">| NULL IS NOT NULL |</span><br><span class="line">+<span class="comment">------------------+</span></span><br><span class="line">|                0 |</span><br><span class="line">+<span class="comment">------------------+</span></span><br><span class="line">mysql&gt; SELECT &#x27;a&#x27; IS NOT NULL;</span><br><span class="line">+<span class="comment">-----------------+</span></span><br><span class="line">| &#x27;a&#x27; IS NOT NULL |</span><br><span class="line">+<span class="comment">-----------------+</span></span><br><span class="line">|               1 |</span><br><span class="line">+<span class="comment">-----------------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>LIKE</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT &#x27;12345&#x27; LIKE &#x27;12%&#x27;;</span><br><span class="line">+<span class="comment">--------------------+</span></span><br><span class="line">| &#x27;12345&#x27; LIKE &#x27;12%&#x27; |</span><br><span class="line">+<span class="comment">--------------------+</span></span><br><span class="line">|                  1 |</span><br><span class="line">+<span class="comment">--------------------+</span></span><br><span class="line">mysql&gt; SELECT &#x27;12345&#x27; LIKE &#x27;12_&#x27;;</span><br><span class="line">+<span class="comment">--------------------+</span></span><br><span class="line">| &#x27;12345&#x27; LIKE &#x27;12_&#x27; |</span><br><span class="line">+<span class="comment">--------------------+</span></span><br><span class="line">|                  0 |</span><br><span class="line">+<span class="comment">--------------------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>REGEXP</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT &#x27;beijing&#x27; REGEXP &#x27;jing&#x27;;</span><br><span class="line">+<span class="comment">-------------------------+</span></span><br><span class="line">| &#x27;beijing&#x27; REGEXP &#x27;jing&#x27; |</span><br><span class="line">+<span class="comment">-------------------------+</span></span><br><span class="line">|                       1 |</span><br><span class="line">+<span class="comment">-------------------------+</span></span><br><span class="line">mysql&gt; SELECT &#x27;beijing&#x27; REGEXP &#x27;xi&#x27;;</span><br><span class="line">+<span class="comment">-----------------------+</span></span><br><span class="line">| &#x27;beijing&#x27; REGEXP &#x27;xi&#x27; |</span><br><span class="line">+<span class="comment">-----------------------+</span></span><br><span class="line">|                     0 |</span><br><span class="line">+<span class="comment">-----------------------+</span></span><br></pre></td></tr></table></figure></p></li></ol><h3 id="逻辑运算符"><a href="#逻辑运算符" class="headerlink" title="逻辑运算符"></a>逻辑运算符</h3><p>逻辑运算符用来判断表达式的真假。如果表达式是真，结果返回 <code>1</code>。如果表达式是假，结果返回 <code>0</code>。</p><div class="table-container"><table><thead><tr><th style="text-align:center">运算符号</th><th style="text-align:center">作用</th></tr></thead><tbody><tr><td style="text-align:center">NOT 或 !</td><td style="text-align:center">逻辑非</td></tr><tr><td style="text-align:center">AND</td><td style="text-align:center">逻辑与</td></tr><tr><td style="text-align:center">OR</td><td style="text-align:center">逻辑或</td></tr><tr><td style="text-align:center">XOR</td><td style="text-align:center">逻辑异或</td></tr></tbody></table></div><ol><li><p>与</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2 AND 0;</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">| 2 AND 0 |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">|       0 |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">mysql&gt; SELECT 2 AND 1;</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">| 2 AND 1 |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">|       1 |</span><br><span class="line">+<span class="comment">---------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>或</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 2 OR 0;</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">| 2 OR 0 |</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">|      1 |</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">mysql&gt; SELECT 2 OR 1;</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">| 2 OR 1 |</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">|      1 |</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">mysql&gt; SELECT 0 OR 0;</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">| 0 OR 0 |</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">|      0 |</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">mysql&gt; SELECT 1 || 0;</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">| 1 || 0 |</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">|      1 |</span><br><span class="line">+<span class="comment">--------+</span></span><br><span class="line">mysql&gt; SELECT NULL OR 1;</span><br><span class="line">+<span class="comment">-----------+</span></span><br><span class="line">| NULL OR 1 |</span><br><span class="line">+<span class="comment">-----------+</span></span><br><span class="line">|         1 |</span><br><span class="line">+<span class="comment">-----------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>非</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT NOT 1;</span><br><span class="line">+<span class="comment">-------+</span></span><br><span class="line">| NOT 1 |</span><br><span class="line">+<span class="comment">-------+</span></span><br><span class="line">|     0 |</span><br><span class="line">+<span class="comment">-------+</span></span><br><span class="line">mysql&gt; SELECT !0;</span><br><span class="line">+<span class="comment">----+</span></span><br><span class="line">| !0 |</span><br><span class="line">+<span class="comment">----+</span></span><br><span class="line">|  1 |</span><br><span class="line">+<span class="comment">----+</span></span><br></pre></td></tr></table></figure></p></li><li><p>异或</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 1 XOR 1;</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">| 1 XOR 1 |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">|       0 |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">mysql&gt; SELECT 0 XOR 0;</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">| 0 XOR 0 |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">|       0 |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">mysql&gt; SELECT 1 XOR 0;</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">| 1 XOR 0 |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">|       1 |</span><br><span class="line">+<span class="comment">---------+</span></span><br><span class="line">mysql&gt; SELECT NULL XOR 1;</span><br><span class="line">+<span class="comment">------------+</span></span><br><span class="line">| NULL XOR 1 |</span><br><span class="line">+<span class="comment">------------+</span></span><br><span class="line">|       NULL |</span><br><span class="line">+<span class="comment">------------+</span></span><br><span class="line">mysql&gt; SELECT 1 ^ 0;</span><br><span class="line">+<span class="comment">-------+</span></span><br><span class="line">| 1 ^ 0 |</span><br><span class="line">+<span class="comment">-------+</span></span><br><span class="line">|     1 |</span><br><span class="line">+<span class="comment">-------+</span></span><br></pre></td></tr></table></figure></p></li></ol><h3 id="位运算符"><a href="#位运算符" class="headerlink" title="位运算符"></a>位运算符</h3><p>位运算符是在二进制数上进行计算的运算符。位运算会先将操作数变成二进制数，进行位运算。然后再将计算结果从二进制数变回十进制数。</p><div class="table-container"><table><thead><tr><th style="text-align:center">运算符号</th><th style="text-align:center">作用</th></tr></thead><tbody><tr><td style="text-align:center">&amp;</td><td style="text-align:center">按位与</td></tr><tr><td style="text-align:center">&vert;</td><td style="text-align:center">按位或</td></tr><tr><td style="text-align:center">^</td><td style="text-align:center">按位异或</td></tr><tr><td style="text-align:center">!</td><td style="text-align:center">取反</td></tr><tr><td style="text-align:center">&lt;&lt;</td><td style="text-align:center">左移</td></tr><tr><td style="text-align:center">&gt;&gt;</td><td style="text-align:center">右移</td></tr></tbody></table></div><ol><li><p>按位与</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 3&amp;5;</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">| 3&amp;5 |</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">|   1 |</span><br><span class="line">+<span class="comment">-----+</span></span><br></pre></td></tr></table></figure></p></li><li><p>按位或</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 3|5;</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">| 3|5 |</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">|   7 |</span><br><span class="line">+<span class="comment">-----+</span></span><br></pre></td></tr></table></figure></p></li><li><p>按位异或</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 3^5;</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">| 3^5 |</span><br><span class="line">+<span class="comment">-----+</span></span><br><span class="line">|   6 |</span><br><span class="line">+<span class="comment">-----+</span></span><br></pre></td></tr></table></figure></p></li><li><p>按位取反</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT ~18446744073709551612;</span><br><span class="line">+<span class="comment">-----------------------+</span></span><br><span class="line">| ~18446744073709551612 |</span><br><span class="line">+<span class="comment">-----------------------+</span></span><br><span class="line">|                     3 |</span><br><span class="line">+<span class="comment">-----------------------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>按位右移</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 3&gt;&gt;1;</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">| 3&gt;&gt;1 |</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">|    1 |</span><br><span class="line">+<span class="comment">------+</span></span><br></pre></td></tr></table></figure></p></li><li><p>按位左移</p><p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; SELECT 3&lt;&lt;1;</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">| 3&lt;&lt;1 |</span><br><span class="line">+<span class="comment">------+</span></span><br><span class="line">|    6 |</span><br><span class="line">+<span class="comment">------+</span></span><br></pre></td></tr></table></figure></p></li></ol><h3 id="运算符优先级"><a href="#运算符优先级" class="headerlink" title="运算符优先级"></a>运算符优先级</h3><p>最低优先级为： <code>:=</code>。</p><div class="table-container"><table><thead><tr><th style="text-align:center">优先级顺序</th><th style="text-align:center">运算符</th></tr></thead><tbody><tr><td style="text-align:center">1</td><td style="text-align:center">:=</td></tr><tr><td style="text-align:center">2</td><td style="text-align:center">&vert;&vert;, OR, XOR</td></tr><tr><td style="text-align:center">3</td><td style="text-align:center">&amp;&amp;, AND</td></tr><tr><td style="text-align:center">4</td><td style="text-align:center">NOT</td></tr><tr><td style="text-align:center">5</td><td style="text-align:center">BETWEEN, CASE, WHEN, THEN, ELSE</td></tr><tr><td style="text-align:center">6</td><td style="text-align:center">=, &lt;=&gt;, &gt;=, &gt;, &lt;=, &lt;, &lt;&gt;, !=, IS, LIKE, REGEXP, IN</td></tr><tr><td style="text-align:center">7</td><td style="text-align:center">&vert;</td></tr><tr><td style="text-align:center">8</td><td style="text-align:center">&amp;</td></tr><tr><td style="text-align:center">9</td><td style="text-align:center">&lt;&lt;, &gt;&gt;</td></tr><tr><td style="text-align:center">10</td><td style="text-align:center">-, +</td></tr><tr><td style="text-align:center">11</td><td style="text-align:center">*, /, DIV, %, MOD</td></tr><tr><td style="text-align:center">12</td><td style="text-align:center">^</td></tr><tr><td style="text-align:center">13</td><td style="text-align:center">- (一元减号)，~ (一元比特反转)</td></tr><tr><td style="text-align:center">14</td><td style="text-align:center">!</td></tr></tbody></table></div><h2 id="关键字"><a href="#关键字" class="headerlink" title="关键字"></a>关键字</h2><h3 id="关键字汇总"><a href="#关键字汇总" class="headerlink" title="关键字汇总"></a>关键字汇总</h3><div class="table-container"><table><thead><tr><th style="text-align:center">关键字</th><th style="text-align:center">关键字</th><th style="text-align:center">关键字</th></tr></thead><tbody><tr><td style="text-align:center">ADD</td><td style="text-align:center">ALL</td><td style="text-align:center">ALTER</td></tr><tr><td style="text-align:center">ANALYZE</td><td style="text-align:center">AND</td><td style="text-align:center">AS</td></tr><tr><td style="text-align:center">ASC</td><td style="text-align:center">ASENSITIVE</td><td style="text-align:center">BEFORE</td></tr><tr><td style="text-align:center">BETWEEN</td><td style="text-align:center">BIGINT</td><td style="text-align:center">BINARY</td></tr><tr><td style="text-align:center">BLOB</td><td style="text-align:center">BOTH</td><td style="text-align:center">BY</td></tr><tr><td style="text-align:center">CALL</td><td style="text-align:center">CASCADE</td><td style="text-align:center">CASE</td></tr><tr><td style="text-align:center">CHANGE</td><td style="text-align:center">CHAR</td><td style="text-align:center">CHARACTER</td></tr><tr><td style="text-align:center">CHECK</td><td style="text-align:center">COLLATE</td><td style="text-align:center">COLUMN</td></tr><tr><td style="text-align:center">CONDITION</td><td style="text-align:center">CONNECTION</td><td style="text-align:center">CONSTRAINT</td></tr><tr><td style="text-align:center">CONTINUE</td><td style="text-align:center">CONVERT</td><td style="text-align:center">CREATE</td></tr><tr><td style="text-align:center">CROSS</td><td style="text-align:center">CURRENT_DATE</td><td style="text-align:center">CURRENT_TIME</td></tr><tr><td style="text-align:center">CURRENT_TIMESTAMP</td><td style="text-align:center">CURRENT_USER</td><td style="text-align:center">CURSOR</td></tr><tr><td style="text-align:center">DATABASE</td><td style="text-align:center">DATABASES</td><td style="text-align:center">DAY_HOUR</td></tr><tr><td style="text-align:center">DAY_MICROSECOND</td><td style="text-align:center">DAY_MINUTE</td><td style="text-align:center">DAY_SECOND</td></tr><tr><td style="text-align:center">DEC</td><td style="text-align:center">DECIMAL</td><td style="text-align:center">DECLARE</td></tr><tr><td style="text-align:center">DEFAULT</td><td style="text-align:center">DELAYED</td><td style="text-align:center">DELETE</td></tr><tr><td style="text-align:center">DESC</td><td style="text-align:center">DESCRIBE</td><td style="text-align:center">DETERMINISTIC</td></tr><tr><td style="text-align:center">DISTINCT</td><td style="text-align:center">DISTINCTROW</td><td style="text-align:center">DIV</td></tr><tr><td style="text-align:center">DOUBLE</td><td style="text-align:center">DROP</td><td style="text-align:center">DUAL</td></tr><tr><td style="text-align:center">EACH</td><td style="text-align:center">ELSE</td><td style="text-align:center">ELSEIF</td></tr><tr><td style="text-align:center">ENCLOSED</td><td style="text-align:center">ESCAPED</td><td style="text-align:center">EXISTS</td></tr><tr><td style="text-align:center">EXIT</td><td style="text-align:center">EXPLAIN</td><td style="text-align:center">FALSE</td></tr><tr><td style="text-align:center">FETCH</td><td style="text-align:center">FLOAT</td><td style="text-align:center">FLOAT4</td></tr><tr><td style="text-align:center">FLOAT8</td><td style="text-align:center">FOR</td><td style="text-align:center">FORCE</td></tr><tr><td style="text-align:center">FOREIGN</td><td style="text-align:center">FROM</td><td style="text-align:center">FULLTEXT</td></tr><tr><td style="text-align:center">GOTO</td><td style="text-align:center">GRANT</td><td style="text-align:center">GROUP</td></tr><tr><td style="text-align:center">HAVING</td><td style="text-align:center">HIGH_PRIORITY</td><td style="text-align:center">HOUR_MICROSECOND</td></tr><tr><td style="text-align:center">HOUR_MINUTE</td><td style="text-align:center">HOUR_SECOND</td><td style="text-align:center">IF</td></tr><tr><td style="text-align:center">IGNORE</td><td style="text-align:center">IN</td><td style="text-align:center">INDEX</td></tr><tr><td style="text-align:center">INFILE</td><td style="text-align:center">INNER</td><td style="text-align:center">INOUT</td></tr><tr><td style="text-align:center">INSENSITIVE</td><td style="text-align:center">INSERT</td><td style="text-align:center">INT</td></tr><tr><td style="text-align:center">INT1</td><td style="text-align:center">INT2</td><td style="text-align:center">INT3</td></tr><tr><td style="text-align:center">INT4</td><td style="text-align:center">INT8</td><td style="text-align:center">INTEGER</td></tr><tr><td style="text-align:center">INTERVAL</td><td style="text-align:center">INTO</td><td style="text-align:center">IS</td></tr><tr><td style="text-align:center">ITERATE</td><td style="text-align:center">JOIN</td><td style="text-align:center">KEY</td></tr><tr><td style="text-align:center">KEYS</td><td style="text-align:center">KILL</td><td style="text-align:center">LABEL</td></tr><tr><td style="text-align:center">LEADING</td><td style="text-align:center">LEAVE</td><td style="text-align:center">LEFT</td></tr><tr><td style="text-align:center">LIKE</td><td style="text-align:center">LIMIT</td><td style="text-align:center">LINEAR</td></tr><tr><td style="text-align:center">LINES</td><td style="text-align:center">LOAD</td><td style="text-align:center">LOCALTIME</td></tr><tr><td style="text-align:center">LOCALTIMESTAMP</td><td style="text-align:center">LOCK</td><td style="text-align:center">LONG</td></tr><tr><td style="text-align:center">LONGBLOB</td><td style="text-align:center">LONGTEXT</td><td style="text-align:center">LOOP</td></tr><tr><td style="text-align:center">LOW_PRIORITY</td><td style="text-align:center">MATCH</td><td style="text-align:center">MEDIUMBLOB</td></tr><tr><td style="text-align:center">MEDIUMINT</td><td style="text-align:center">MEDIUMTEXT</td><td style="text-align:center">MIDDLEINT</td></tr><tr><td style="text-align:center">MINUTE_MICROSECOND</td><td style="text-align:center">MINUTE_SECOND</td><td style="text-align:center">MOD</td></tr><tr><td style="text-align:center">MODIFIES</td><td style="text-align:center">NATURAL</td><td style="text-align:center">NOT</td></tr><tr><td style="text-align:center">NO_WRITE_TO_BINLOG</td><td style="text-align:center">NULL</td><td style="text-align:center">NUMERIC</td></tr><tr><td style="text-align:center">ON</td><td style="text-align:center">OPTIMIZE</td><td style="text-align:center">OPTION</td></tr><tr><td style="text-align:center">OPTIONALLY</td><td style="text-align:center">OR ORDER</td><td style="text-align:center">&nbsp;</td></tr><tr><td style="text-align:center">OUT</td><td style="text-align:center">OUTER</td><td style="text-align:center">OUTFILE</td></tr><tr><td style="text-align:center">PRECISION</td><td style="text-align:center">PRIMARY</td><td style="text-align:center">PROCEDURE</td></tr><tr><td style="text-align:center">PURGE</td><td style="text-align:center">RAID0</td><td style="text-align:center">RANGE</td></tr><tr><td style="text-align:center">READ</td><td style="text-align:center">READS</td><td style="text-align:center">REAL</td></tr><tr><td style="text-align:center">REFERENCES</td><td style="text-align:center">REGEXP</td><td style="text-align:center">RELEASE</td></tr><tr><td style="text-align:center">RENAME</td><td style="text-align:center">REPEAT</td><td style="text-align:center">REPLACE</td></tr><tr><td style="text-align:center">REQUIRE</td><td style="text-align:center">RESTRICT</td><td style="text-align:center">RETURN</td></tr><tr><td style="text-align:center">REVOKE</td><td style="text-align:center">RIGHT</td><td style="text-align:center">RLIKE</td></tr><tr><td style="text-align:center">SCHEMA</td><td style="text-align:center">SCHEMAS</td><td style="text-align:center">SECOND_MICROSECOND</td></tr><tr><td style="text-align:center">SELECT</td><td style="text-align:center">SENSITIVE</td><td style="text-align:center">SEPARATOR</td></tr><tr><td style="text-align:center">SET</td><td style="text-align:center">SHOW</td><td style="text-align:center">SMALLINT</td></tr><tr><td style="text-align:center">SPATIAL</td><td style="text-align:center">SPECIFIC</td><td style="text-align:center">SQL</td></tr><tr><td style="text-align:center">SQLEXCEPTION</td><td style="text-align:center">SQLSTATE</td><td style="text-align:center">SQLWARNING</td></tr><tr><td style="text-align:center">SQL_BIG_RESULT</td><td style="text-align:center">SQL_CALC_FOUND_ROWS</td><td style="text-align:center">SQL_SMALL_RESULT</td></tr><tr><td style="text-align:center">SSL</td><td style="text-align:center">STARTING</td><td style="text-align:center">STRAIGHT_JOIN</td></tr><tr><td style="text-align:center">TABLE</td><td style="text-align:center">TERMINATED</td><td style="text-align:center">THEN</td></tr><tr><td style="text-align:center">TINYBLOB</td><td style="text-align:center">TINYINT</td><td style="text-align:center">TINYTEXT</td></tr><tr><td style="text-align:center">TO</td><td style="text-align:center">TRAILING</td><td style="text-align:center">TRIGGER</td></tr><tr><td style="text-align:center">TRUE</td><td style="text-align:center">UNDO</td><td style="text-align:center">UNION</td></tr><tr><td style="text-align:center">UNIQUE</td><td style="text-align:center">UNLOCK</td><td style="text-align:center">UNSIGNED</td></tr><tr><td style="text-align:center">UPDATE</td><td style="text-align:center">USAGE</td><td style="text-align:center">USE</td></tr><tr><td style="text-align:center">USING</td><td style="text-align:center">UTC_DATE</td><td style="text-align:center">UTC_TIME</td></tr><tr><td style="text-align:center">UTC_TIMESTAMP</td><td style="text-align:center">VALUES</td><td style="text-align:center">VARBINARY</td></tr><tr><td style="text-align:center">VARCHAR</td><td style="text-align:center">VARCHARACTER</td><td style="text-align:center">VARYING</td></tr><tr><td style="text-align:center">WHEN</td><td style="text-align:center">WHERE</td><td style="text-align:center">WHILE</td></tr><tr><td style="text-align:center">WITH</td><td style="text-align:center">WRITE</td><td style="text-align:center">X509</td></tr><tr><td style="text-align:center">XOR</td><td style="text-align:center">YEAR_MONTH</td><td style="text-align:center">ZEROFILL</td></tr></tbody></table></div><h3 id="执行顺序"><a href="#执行顺序" class="headerlink" title="执行顺序"></a>执行顺序</h3><p>在 SQL 语句中每个关键字都会按照顺序往下执行，而每一步操作，会生成一个虚拟表，最后的虚拟表就是最终结果。</p><p>基本 SQL 语句如下：</p><p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">(8)<span class="keyword">SELECT</span> (<span class="number">9</span>)<span class="keyword">DISTINCT</span> &lt;select_list&gt;</span><br><span class="line">(<span class="number">1</span>)<span class="keyword">FROM</span> &lt;left_table&gt;</span><br><span class="line">(<span class="number">3</span>)&lt;join_type&gt; <span class="keyword">JOIN</span> &lt;right_table&gt;</span><br><span class="line">(<span class="number">2</span>)<span class="keyword">ON</span> &lt;join_condition&gt;</span><br><span class="line">(<span class="number">4</span>)<span class="keyword">WHERE</span> &lt;where_condition&gt;</span><br><span class="line">(<span class="number">5</span>)<span class="keyword">GROUP</span> <span class="keyword">BY</span> &lt;group_by_list&gt;</span><br><span class="line">(<span class="number">6</span>)<span class="keyword">WITH</span>&#123;<span class="keyword">CUBE</span>|<span class="keyword">ROLLUP</span>&#125;</span><br><span class="line">(<span class="number">7</span>)<span class="keyword">HAVING</span> &lt;having_condition&gt;</span><br><span class="line">(<span class="number">10</span>)<span class="keyword">ORDER</span> <span class="keyword">BY</span> &lt;order_by_list&gt;</span><br><span class="line">(<span class="number">11</span>)<span class="keyword">LIMIT</span> &lt;limit_number&gt;</span><br></pre></td></tr></table></figure></p><p>执行顺序：</p><ol><li>FROM： 对 <code>FROM</code> 左边的表和右边的表计算笛卡尔积，产生虚表 <code>VT1</code>；</li><li>ON： 对虚拟表 <code>VT1</code> 进行 <code>ON</code> 筛选，只有那些符合条件的行才会被记录在虚拟表 <code>VT2</code> 中；</li><li>JOIN：如果是 <code>OUT JOIN</code>，那么将保留表中（如左表或者右表）未匹配的行作为外部行添加到虚拟表 <code>VT2</code> 中，从而产生虚拟表 <code>VT3</code>；</li><li>WHERE：对虚拟表 <code>VT3</code> 进行 <code>WHERE</code> 条件过滤，只有符合的记录才会被放入到虚拟表 VT4；</li><li>GROUP BY：根据 <code>GROUP BY</code> 子句中的列，对虚拟表 <code>VT4</code> 进行分组操作，产生虚拟表 <code>VT5</code>；</li><li>CUBE | ROLLUP：对虚拟表 <code>VT5</code> 进行 <code>CUBE</code> 或者 <code>ROLLUP</code> 操作，产生虚拟表 <code>VT6</code>；</li><li>HAVING：对虚拟表 <code>VT6</code> 进行 <code>HAVING</code> 条件过滤，只有符合的记录才会被插入到虚拟表 <code>VT7</code> 中；</li><li>SELECT：执行 <code>SELECT</code> 操作，选择指定的列，插入到虚拟表 <code>VT8</code> 中；</li><li>DISTINCT：对虚拟表 <code>VT8</code> 中的记录进行去重，产生虚拟表 <code>VT9</code>；</li><li>ORDER BY：将虚拟表 <code>VT9</code> 中的记录按照进行排序操作，产生虚拟表 <code>VT10</code>；</li><li>LIMIT：取出指定行的记录，产生虚拟表 <code>VT11</code>，并将结果返回。</li></ol>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/MySQL/">MySQL</category>
      
      
      <comments>https://blog.eurkon.com/post/7f59cefa.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>大数据精选清单</title>
      <link>https://blog.eurkon.com/post/fc3d946f.html</link>
      <guid>https://blog.eurkon.com/post/fc3d946f.html</guid>
      <pubDate>Thu, 07 Jan 2021 02:35:46 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;精选的大数据工具、框架、资源和其他清单。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文翻译自：&lt;a href=&quot;https://github</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>精选的大数据工具、框架、资源和其他清单。</p><blockquote><p>本文翻译自：<a href="https://github.com/onurakpolat/awesome-bigdata">Awesome Big Data</a><br>灵感来自：<a href="https://github.com/ziadoz/awesome-php">awesome-php</a> &amp; <a href="https://github.com/vinta/awesome-python">awesome-python</a> &amp; <a href="https://github.com/Sdogruyol/awesome-ruby">awesome-ruby</a> &amp; <a href="http://hadoopecosystemtable.github.io/">hadoopecosystemtable</a> &amp; <a href="http://usefulstuff.io/big-data/">big-data</a></p></blockquote><h2 id="关系型数据库"><a href="#关系型数据库" class="headerlink" title="关系型数据库"></a>关系型数据库</h2><ul><li><a href="https://www.mysql.com/">MySQL</a> - 世界上最流行的开源数据库。</li><li><a href="https://www.postgresql.org/">PostgreSQL</a> - 世界上最先进的开源数据库。</li><li><a href="http://www.oracle.com/us/corporate/features/database-12c/index.html">Oracle Database</a> - 对象关系数据库管理系统。</li><li><a href="http://www.teradata.com/products-and-services/teradata-database/">Teradata</a> - 高性能 MPP 数据仓库平台。</li></ul><h2 id="框架"><a href="#框架" class="headerlink" title="框架"></a>框架</h2><ul><li><a href="https://github.com/facebook/bistro">Bistro</a> - 用于批处理和流分析的通用数据处理引擎。它基于一种新的数据模型，该模型通过函数来表示数据，并通过列操作来处理数据，而不仅仅使用 MapReduce 或 SQL 等传统方法来设置操作。</li><li><a href="https://www.ibm.com/analytics/us/en/technology/stream-computing/">IBM Streams</a> - 分布式处理和实时分析平台。可以和大数据生态系统中的许多流行技术（Kafka、HDFS、Spark 等）集成。</li><li><a href="http://hadoop.apache.org/">Apache Hadoop</a> - 分布式处理框架。集成了 MapReduce（并行处理）、YARN（作业调度）和 HDFS（分布式文件系统）。</li><li><a href="https://github.com/caskdata/tigon">Tigon</a> - 高吞吐的实时流处理框架。</li><li><a href="http://pachyderm.io/">Pachyderm</a> - Pachyderm 是一个基于 Docker 和 Kubernetes 的数据存储平台，可以用在重复的数据处理和分析场景。</li><li><a href="https://github.com/polyaxon/polyaxon">Polyaxon</a> - 一个可复制、可扩展的机器学习和深度学习平台。</li></ul><h2 id="分布式编程"><a href="#分布式编程" class="headerlink" title="分布式编程"></a>分布式编程</h2><ul><li><a href="https://github.com/addthis/hydra">AddThis Hydra</a> - 分布式数据处理和存储系统，最初由 AddThis 开发。</li><li><a href="http://databricks.github.io/simr/">AMPLab SIMR</a> - 在 Hadoop MapReduce v1 上运行 Spark。</li><li><a href="https://apex.apache.org/">Apache APEX</a> - 用于大数据流和批处理的统一企业平台。</li><li><a href="https://beam.apache.org/">Apache Beam</a> - 用于定义和执行数据处理工作流的统一模型和一组特定于语言的 sdk。</li><li><a href="http://crunch.apache.org/">Apache Crunch</a> - 一个简单的 Java API，用于处理 Join 和数据聚合之类的任务，这些任务在普通 MapReduce 上实现起来很繁琐。</li><li><a href="http://incubator.apache.org/projects/datafu.html">Apache DataFu</a> - 由 LinkedIn 为 Hadoop 和 Pig 开发的用户定义函数的集合。</li><li><a href="http://flink.apache.org/">Apache Flink</a> - 分布式处理引擎框架，用于在无界和有界数据流上进行有状态计算。</li><li><a href="http://gearpump.apache.org/">Apache Gearpump</a> - 基于 Akka 的实时大数据流引擎。</li><li><a href="http://gora.apache.org/">Apache Gora</a> - 内存数据模型和持久性框架。</li><li><a href="http://hama.apache.org/">Apache Hama</a> - BSP（Bulk Synchronous Parallel）计算框架。</li><li><a href="https://wiki.apache.org/hadoop/MapReduce/">Apache MapReduce</a> - 在集群上使用并行分布式算法处理大型数据集的编程模型。</li><li><a href="https://pig.apache.org/">Apache Pig</a> - 用于表达 Hadoop 数据分析程序的高级语言。</li><li><a href="http://reef.apache.org/">Apache REEF</a> - 用来简化和统一低层大数据系统的保留性评估执行框架。</li><li><a href="http://incubator.apache.org/projects/s4.html">Apache S4</a> - 一个常规用途的、分布式的、可伸缩的、容错的、可插入式的平台，主要用于处理连续的数据流。</li><li><a href="http://spark.apache.org/">Apache Spark</a> - 快速、通用的大规模数据处理引擎。</li><li><a href="https://spark.apache.org/docs/latest/streaming-programming-guide.html">Apache Spark Streaming</a> - 实时流处理引擎，属于 Spark 的一部分。</li><li><a href="http://storm.apache.org">Apache Storm</a> - Twitter 开发的，可在 YARN 上进行流处理的框架。</li><li><a href="http://samza.apache.org/">Apache Samza</a> - 基于 Kafka 和 YARN 的流处理的框架。</li><li><a href="http://tez.apache.org/">Apache Tez</a> - 基于 YARN 的，可执行复杂 DAG（有向无环图）任务的应用程序框架。</li><li><a href="https://incubator.apache.org/projects/twill.html">Apache Twill</a> - YARN 上的抽象，减少了开发分布式应用程序的复杂性。</li><li><a href="http://bigflow.cloud/en/index.html">Baidu Bigflow</a> - 一个允许编写分布式计算程序的接口，它提供了许多简单、灵活、强大的 API 来轻松处理任何规模的数据。</li><li><a href="http://cascalog.org/">Cascalog</a> - 数据处理和查询库。</li><li><a href="http://vldbarc.org/pvldb/vldb2010/pvldb_vol3/I08.pdf">Cheetah</a> - MapReduce 之上的高性能，用户自定义数据仓库。</li><li><a href="http://www.cascading.org/">Concurrent Cascading</a> - Hadoop 上的数据管理 / 分析框架。</li><li><a href="https://github.com/damballa/parkour">Damballa Parkour</a> - 为 Clojure 开发的 MapReduce 库。</li><li><a href="https://github.com/datasalt/pangool">Datasalt Pangool</a> - 可替代 MapReduce 范式。</li><li><a href="https://www.datatorrent.com/">DataTorrent StrAM</a> - 实时计算引擎，旨在以一种尽可能畅通的方式支持分布式、异步、实时的内存大数据计算，同时最小化开销和对性能的影响。</li><li><a href="https://www.facebook.com/notes/facebook-engineering/under-the-hood-scheduling-mapreduce-jobs-more-efficiently-with-corona/10151142560538920">Facebook Corona</a> - Hadoop 的增强，可以消除单点故障。</li><li><a href="http://peregrine_mapreduce.bitbucket.org/">Facebook Peregrine</a> - Map Reduce 框架。</li><li><a href="https://www.facebook.com/notes/facebook-engineering/under-the-hood-data-diving-with-scuba/10150599692628920">Facebook Scuba</a> - 分布式内存数据存储。</li><li><a href="https://googledevelopers.blogspot.it/2014/06/cloud-platform-at-google-io-new-big.html">Google Dataflow</a> - 创建数据管道来帮助我们摄取、转换和分析数据。</li><li><a href="https://research.google.com/archive/mapreduce.html">Google MapReduce</a> - Map Reduce 框架。</li><li><a href="https://research.google.com/pubs/pub41378.html">Google MillWheel</a> - 容错流处理框架。</li><li><a href="https://www.ibm.com/analytics/us/en/technology/stream-computing/">IBM Streams</a> - 用于分布式处理和实时分析的平台。 提供开箱即用的高级分析工具包，如地理空间、时间序列等。</li><li><a href="https://code.google.com/p/jaql/">JAQL</a> - 声明式编程语言，用于处理结构化、半结构化和非结构化数据。</li><li><a href="http://kitesdk.org/docs/current/">Kite</a> - 一组库、工具、示例和文档，重点在于简化在 Hadoop 生态系统之上构建系统的过程。</li><li><a href="http://druid.io/">Metamarkets Druid</a> - 用于实时分析大型数据集的框架。</li><li><a href="https://github.com/Netflix/PigPen">Netflix PigPen</a> - 是 Clojure 语音的 Map-Reduce，可以编译到 Apache Pig 或者 Cascading 中。</li><li><a href="http://discoproject.org/">Nokia Disco</a> - 诺基亚开发的 MapReduce 框架。</li><li><a href="http://www.onyxplatform.org/">Onyx</a> - 云的分布式计算。</li><li><a href="https://medium.com/@Pinterest_Engineering/pinlater-an-asynchronous-job-execution-system-b8664cb8aa7d">Pinterest Pinlater</a> - 异步作业执行系统。</li><li><a href="http://crs4.github.io/pydoop/">Pydoop</a> - 用 Python 编写，并采用 MapReduce 和 HDFS 技术对 Hadoop 进行扩展的 API。</li><li><a href="https://github.com/ray-project/ray">Ray</a> - 用于构建和运行分布式应用程序的快速而简单的框架。</li><li><a href="http://blueflood.io/">Rackerlabs Blueflood</a> - 多租户分布式度量处理系统。</li><li><a href="https://github.com/skale-me/skale-engine">Skale</a> - NodeJS 上的高性能分布式数据处理框架。</li><li><a href="http://stratosphere.eu/">Stratosphere</a> - 通用集群计算框架。</li><li><a href="https://streamdrill.com/">Streamdrill</a> - Streamdrill 在计算不同时间窗口上的事件流活动非常有用，并找出最活跃的时间窗口。</li><li><a href="https://github.com/IBMStreams/streamsx.topology">streamsx.topology</a> - 用于在 Java，Python 或 Scala 中构建 IBM Streams 应用程序的库。</li><li><a href="https://github.com/UnderstandLingBV/Tuktu">Tuktu</a> - 易于使用的批处理和流式计算平台，可以使用 Scala，Akka 和 Play 构建。</li><li><a href="https://github.com/twitter/heron">Twitter Heron</a> - 由 Twitter 开发的一个实时、分布式、容错的流处理引擎，主要用于代替 Storm。</li><li><a href="https://github.com/twitter/scalding">Twitter Scalding</a> - 用于 Map Reduce 作业的 Scala 库，基于 Cascading 构建。</li><li><a href="https://github.com/twitter/summingbird">Twitter Summingbird</a> - Summingbird 是一个类库，它允许我们编写看起来像原生 Scala 或 Java 集合转换的 MapReduce 程序，并在许多着名的分布式 MapReduce 平台上执行，包括 Storm 和 Scalding，由 Twitter 开发。</li><li><a href="https://blog.twitter.com/engineering/en_us/a/2014/tsar-a-timeseries-aggregator.html">Twitter TSAR</a> - Twitter 开发的时间序列聚合器。</li><li><a href="http://www.wallaroolabs.com/community">Wallaroo</a> - 超快弹性数据处理引擎，可以使有状态、分析、流处理和事件驱动的 AI 应用程序能够快速投入生产，而无需考虑规模。它为开发人员提供了几种语言的 API 来实现他们的自定义业务逻辑。</li></ul><h2 id="分布式文件系统"><a href="#分布式文件系统" class="headerlink" title="分布式文件系统"></a>分布式文件系统</h2><ul><li><a href="https://github.com/linkedin/ambry">Ambry</a> - 分布式对象存储，支持存储数万亿个小的不可变对象或者数十亿个大对象。</li><li><a href="http://hadoop.apache.org/">Apache HDFS</a> - 提供对应用程序数据的高吞吐量访问的分布式文件系统。</li><li><a href="http://kudu.apache.org/">Apache Kudu</a> - Hadoop 的存储层可实现对数据的快速分析。</li><li><a href="https://www.beegfs.io/content/">BeeGFS</a> - 之前称为 FhGFS，是一种并行分布式文件系统。</li><li><a href="http://ceph.com/ceph-storage/file-system/">Ceph Filesystem</a> -一个支持 POSIX 接口的文件系统。</li><li><a href="http://disco.readthedocs.org/en/latest/howto/ddfs.html">Disco DDFS</a> - 分布式文件系统。</li><li><a href="https://www.facebook.com/note.php?note_id=76191543919">Facebook Haystack</a> - 对象存储系统。</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en//archive/gfs-sosp2003.pdf">Google GFS</a> - 分布式文件系统。</li><li><a href="http://static.googleusercontent.com/media/research.google.reverse-proxy.org/en/us/university/relations/facultysummit2010/storage_architecture_and_challenges.pdf">Google Colossus</a> - 分布式文件系统（GFS2）。</li><li><a href="https://research.google.com/pubs/pub36971.html">Google Megastore</a> - 可扩展、高可用的存储。</li><li><a href="https://www.gridgain.com/">GridGain</a> - GGFS，Hadoop 兼容的内存文件系统。</li><li><a href="http://wiki.lustre.org/">Lustre file system</a> - 高性能分布式文件系统。</li><li><a href="https://hadoop.apache.org/docs/current/hadoop-azure-datalake/index.html">Microsoft Azure Data Lake Store</a> - Azure 上兼容 HDFS 的存储。</li><li><a href="https://www.quantcast.com/about-us/quantcast-file-system/">Quantcast File System QFS</a> - 开源分布式文件系统。</li><li><a href="http://gluster.org/">Red Hat GlusterFS</a> - 横向扩展网络附加的存储文件系统。</li><li><a href="https://github.com/chrislusf/seaweedfs">Seaweed-FS</a> - 简单且高度可伸缩的分布式文件系统。</li><li><a href="http://www.alluxio.org/">Alluxio</a> - 开源的基于内存的分布式存储系统。</li><li><a href="https://www.tahoe-lafs.org/trac/tahoe-lafs">Tahoe-LAFS</a> - 去中心化的云存储系统。</li><li><a href="https://github.com/baidu/bfs">Baidu File System</a> - 分布式文件系统。</li></ul><h2 id="分布式索引"><a href="#分布式索引" class="headerlink" title="分布式索引"></a>分布式索引</h2><ul><li><a href="https://github.com/pilosa/pilosa">Pilosa</a> - 开源的分布式位图索引，极大地加速了跨多个大规模数据集的查询。</li></ul><h2 id="文档数据模型"><a href="#文档数据模型" class="headerlink" title="文档数据模型"></a>文档数据模型</h2><ul><li><a href="https://www.actian.com/data-management/ingres-sql-rdbms/">Actian Versant</a> - 面向对象的商业数据库管理系统。</li><li><a href="https://crate.io/">Crate Data</a> - 是一个开源的大规模可扩展数据存储，它不需要任何管理。</li><li><a href="http://www.infoq.com/news/2014/06/facebook-apollo">Facebook Apollo</a> - Facebook 的类似于 Paxos 的 NoSQL 数据库。</li><li><a href="http://comsysto.github.io/jumbodb/">jumboDB</a> - 基于 Hadoop 的面向文档的数据存储。</li><li><a href="https://engineering.linkedin.com/data">LinkedIn Espresso</a> - 可水平扩展的面向文档 NoSQL 数据存储。</li><li><a href="http://www.marklogic.com/">MarkLogic</a> - 模式无关的企业 NoSQL 数据库技术。</li><li><a href="https://azure.microsoft.com/en-us/services/cosmos-db/">Microsoft Azure DocumentDB</a> - NoSQL 云数据库服务，支持 MongoDB 协议</li><li><a href="https://www.mongodb.com/">MongoDB</a> - 面向文档的数据库系统。</li><li><a href="https://ravendb.net/">RavenDB</a> - 支持事务的开源文档数据库。</li><li><a href="https://rethinkdb.com/">RethinkDB</a> - 支持表 join 和 group by 等查询的文档数据库。</li></ul><h2 id="Key-Map-数据模型"><a href="#Key-Map-数据模型" class="headerlink" title="Key Map 数据模型"></a>Key Map 数据模型</h2><p><strong>注意</strong>: 业界存在一些术语混淆，存在两种不同的东西被称为“列式数据库”。这里列出的一些是围绕“键 - 映射”数据模型构建的分布式持久性数据库：所有数据都有一个(可能是组合的)键，键值对的映射与之关联。在某些系统中，多个这样的值映射可以与一个键关联，这些映射称为“列族”（值映射键称为“列”）。</p><p>另一种也称为“列式数据库”的技术，特点是它在磁盘或内存中如何存储数据。这些系统将所有行的相同列值数据存储在一起。因此，需要做更多的工作来获得给定键的所有列，但是需要更少的工作来获得给定列的所有值。</p><p>前一种在这里称为“键映射数据模型”。这些和 <a href="#Key-value-数据模型">Key-value 数据模型</a> 存储之间的界限相当模糊。</p><p>后者更多地是关于存储格式而不是数据模型，这些数据库我们把它归到 <a href="#列式数据库">列式数据库</a> 里面去了。</p><p>你可以到 Prof. Daniel Abadi 的博文：<a href="http://dbmsmusings.blogspot.com/2010/03/distinguishing-two-major-types-of_29.html">Distinguishing two major types of Column Stores</a>。</p><ul><li><a href="http://accumulo.apache.org/">Apache Accumulo</a> - 构建在 Hadoop 之上的分布式键值存储系统。</li><li><a href="http://cassandra.apache.org/">Apache Cassandra</a> - 受 BigTable 启发的、面向列的分布式数据存储。</li><li><a href="http://hbase.apache.org/">Apache HBase</a> - 受 BigTable 启发的、面向列的分布式数据存储。</li><li><a href="https://github.com/baidu/tera">Baidu Tera</a> - 受 BigTable 启发的一种大型分布式表格存储系统，具有高性能、可伸缩等存储特点，最初的设计是为了管理万亿量级的超链和网页信息。</li><li><a href="https://code.facebook.com/posts/321111638043166/hydrabase-the-evolution-of-hbase-facebook/">Facebook HydraBase</a> - 由 Facebook 开发的 HBase 演化版本。</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en//archive/bigtable-osdi06.pdf">Google BigTable</a> - 面向列的分布式数据存储。</li><li><a href="https://cloud.google.com/datastore/docs/concepts/overview">Google Cloud Datastore</a> - 一个完全托管的无模式数据库，用于在 BigTable 上存储非关系数据。</li><li><a href="http://www.hypertable.org/">Hypertable</a> - 受 BigTable 启发的、面向列的分布式数据存储。</li><li><a href="https://github.com/infinidb/infinidb/">InfiniDB</a> - 通过 MySQL 接口访问，并使用大规模并行处理来并行化查询。</li><li><a href="https://github.com/caskdata/tephra">Tephra</a> - 使 HBase 支持事务</li><li><a href="https://blog.twitter.com/engineering/en_us/a/2014/manhattan-our-real-time-multi-tenant-distributed-database-for-twitter-scale.html">Twitter Manhattan</a> - Twitter 开发的实时、多租户分布式数据库。</li><li><a href="http://www.scylladb.com/">ScyllaDB</a> - 使用 C++ 编写的面向列的分布式数据存储，完全兼容 Apache Cassandra。</li></ul><h2 id="Key-value-数据模型"><a href="#Key-value-数据模型" class="headerlink" title="Key-value 数据模型"></a>Key-value 数据模型</h2><ul><li><a href="http://www.aerospike.com/">Aerospike</a> - 一个分布式，高可用的 K-V 类型的 NOSQL 数据库。提供类似传统数据库的 ACID 操作。</li><li><a href="https://aws.amazon.com/dynamodb/">Amazon DynamoDB</a> - 分布式 key/value 存储，Dynamo 论文的实现。</li><li><a href="https://open.dgraph.io/post/badger/">Badger</a> - 一个快速、简单、高效和持久的键值存储，是用 Go 编写。</li><li><a href="https://github.com/boltdb/bolt">Bolt</a> - 可在 Go 语言中使用的嵌入式键值数据库。</li><li><a href="https://github.com/Bobris/BTDB">BTDB</a> - .Net 中的 Key Value 数据库，包含 Object DB Layer，RPC，dynamic IL 等等。</li><li><a href="https://github.com/tidwall/buntdb">BuntDB</a> - Go 语言的一个快速，可嵌入，基于内存的 key/value 数据库，支持自定义索引和地理空间。</li><li><a href="https://github.com/cbd/edis">Edis</a> - 协议兼容 Redis 的数据库，可替代 Redis。</li><li><a href="https://github.com/nathanmarz/elephantdb">ElephantDB</a> - 专门用于从 Hadoop 导出数据的分布式数据库。</li><li><a href="https://geteventstore.com/">EventStore</a> - 分布式时间序列数据库。</li><li><a href="https://github.com/griddb/griddb_nosql">GridDB</a> - 一款高度可扩展的 NoSQL 数据库，非常适用于物联网和大数据领域，还具有高可靠性和高性能这些特性。</li><li><a href="https://github.com/rescrv/HyperDex">HyperDex</a> - 可扩展的下一代键值和文档存储，具有多种功能，包括一致性，容错性和高性能。</li><li><a href="https://ignite.apache.org/index.html">Ignite</a> - 分布式内存网格数据库，具有可持久化，分布式事务，分布式计算等特点，此外还支持丰富的键值存储以及 SQL 语法。</li><li><a href="https://github.com/linkedin-sna/sna-page/tree/master/krati">LinkedIn Krati</a> - 一个简单的持久化数据存储，具有非常低的延迟和高吞吐量。</li><li><a href="http://www.project-voldemort.com/voldemort/">Linkedin Voldemort</a> - 分布式 key/value 存储系统。</li><li><a href="http://www.oracle.com/technetwork/database/database-technologies/nosqldb/overview/index.html">Oracle NoSQL Database</a> - Oracle 公司开发的分布式 key/value 存储系统。</li><li><a href="https://redis.io/">Redis</a> - 一个开源（BSD 许可）的，内存中的数据结构存储系统，它可以用作数据库、缓存和消息中间件。</li><li><a href="https://github.com/basho/riak">Riak</a> - 去中心化的数据库存储。</li><li><a href="https://github.com/twitter/storehaus">Storehaus</a> - Twitter 开发的用于异步 key/value 存储的类库。</li><li><a href="https://github.com/tidwall/summitdb">SummitDB</a> - 基于内存的 NoSQL 键/值数据库，具有磁盘持久性，并支持 Raft 一致性算法。</li><li><a href="https://github.com/tarantool/tarantool">Tarantool</a> - 一个高效的 NoSQL 数据库和一个 Lua 应用服务器。</li><li><a href="https://github.com/pingcap/tikv">TiKV</a> - 一个基于 Rust 的分布式键值数据库，并受谷歌 Spanner 和 HBase 的启发。</li><li><a href="https://github.com/tidwall/tile38">Tile38</a> - 具有空间索引和实时地理围栏的地理位置数据库。支持各种对象类型，包括纬度/经度点，边界框，XYZ 切片，Geohashes 和 GeoJSON。</li><li><a href="https://github.com/Treode/store">TreodeDB</a> - key-value 存储，支持数据副本、分片以及提供原子多行写。</li></ul><h2 id="图数据模型"><a href="#图数据模型" class="headerlink" title="图数据模型"></a>图数据模型</h2><ul><li><a href="http://www.agensgraph.com/">AgensGraph</a> - 基于 PostgreSQL 的新一代多模型图数据库。</li><li><a href="http://giraph.apache.org/">Apache Giraph</a> - 一个可伸缩的分布式迭代图处理系统，基于 Hadoop 平台，灵感来自 BSP (bulk synchronous parallel) 和 Google 的 Pregel。</li><li><a href="http://spark.apache.org/docs/0.7.3/bagel-programming-guide.html">Apache Spark Bagel</a> - Bagel 是谷歌 Pregel 图处理框架的 Spark 实现，支持基本的图形计算、组合器（combiners）和聚合器（aggregators）。目前已经被 GraphX 替代，在 Spark 2.0.0 版本已经被移除。</li><li><a href="https://www.arangodb.com/">ArangoDB</a> - 多模型分布式数据库。</li><li><a href="https://github.com/dgraph-io/dgraph">DGraph</a> - 一个可伸缩的、分布式的、低延迟的、高吞吐量的图数据库，旨在提供谷歌生产级别的规模和吞吐量，具有足够低的延迟，可以在 TB 级的结构化数据上为实时用户查询提供服务。</li><li><a href="https://github.com/krotik/eliasdb">EliasDB</a> - 一个轻量级的基于图的数据库，不需要任何第三方库。</li><li><a href="https://www.facebook.com/notes/facebook-engineering/tao-the-power-of-the-graph/10151525983993920">Facebook TAO</a> - TAO 是 Facebook 广泛使用的分布式数据存储，用于存储和服务社交图。</li><li><a href="https://github.com/gchq/Gaffer">GCHQ Gaffer</a> - Gaffer 是 GCHQ（英国政府通讯总部）于 2015 年 12 月 14 日在 GitHub 上公布的第一个开源项目，Gaffer 是个大规模图形数据库，可以方便存储大规模图的框架，节点和边界有数据统计，比如计数，直方图和草图。这些统计数据是时间窗口的节点和边界属性，可以根据时间动态更新。</li><li><a href="https://github.com/cayleygraph/cayley">Google Cayley</a> - 开源的图数据库。</li><li><a href="http://kowshik.github.io/JPregel/pregel_paper.pdf">Google Pregel</a> - 图处理框架。</li><li><a href="https://turi.com/products/create/docs/">GraphLab PowerGraph</a> - 包含 C++ 实现的 GraphLab API 以及一组基于 GraphLab API 构建的高性能机器学习和数据挖掘工具包。</li><li><a href="https://amplab.cs.berkeley.edu/publication/graphx-grades/">GraphX</a> - 一个分布式图处理框架，它是基于 Spark 平台提供对图计算和图挖掘简洁易用的而丰富的接口，极大的方便了对分布式图处理的需求。</li><li><a href="https://github.com/tinkerpop/gremlin">Gremlin</a> - 图遍历语言。</li><li><a href="https://github.com/paulhoule/infovore">Infovore</a> - 一个 Map/Reduce 框架，用来处理大量的 RDF 数据集，注入 Freebase 和 DBpedia，基于 Hadoop 构建。</li><li><a href="https://01.org/graphbuilder/">Intel GraphBuilder</a> - 基于 Hadoop 构造的大型图工具。</li><li><a href="http://janusgraph.org">JanusGraph</a> - 开源分布式图形数据库，后端存储可以选择多种组件包括 Bigtable、HBase、Cassandra 等，同时索引后端也可以选择很多种，包括 Elasticsearch、Solr、Lucene 等。</li><li><a href="https://www.blazegraph.com/mapgraph-technology/">MapGraph</a> - 一个高级的 API 用于快速开发基于 GPU 的高性能图形分析应用。</li><li><a href="https://github.com/Microsoft/GraphEngine">Microsoft Graph Engine</a> - 一个基于内存的分布式大规模图数据处理引擎，能够帮助用户更方便地构建实时查询应用和高吞吐量离线分析平台。在此之前，它在学术界更广为人之的名称是 Trinity。</li><li><a href="https://neo4j.com/">Neo4j</a> - 一个高性能的 NOSQL 图数据库，完全由 Java 实现。</li><li><a href="http://orientdb.com/">OrientDB</a> - 文档图形数据库。</li><li><a href="https://github.com/xslogic/phoebus">Phoebus</a> - 大型图处理框架。</li><li><a href="http://thinkaurelius.github.io/titan/">Titan</a> - 建立在 Cassandra 之上的分布式图数据库。</li><li><a href="https://github.com/twitter-archive/flockdb">Twitter FlockDB</a> - 分布式图数据库。</li><li><a href="https://nodexl.codeplex.com/">NodeXL</a> - Microsoft® Excel® 2007，2010，2013 和 2016 免费开源的模板，可以很容易的探索网络图。</li></ul><h2 id="列式数据库"><a href="#列式数据库" class="headerlink" title="列式数据库"></a>列式数据库</h2><p><strong>注意</strong>：请读一下 <a href="#Key-value-数据模型">Key-value 数据模型</a> 章节的说明。</p><ul><li><a href="http://the-paper-trail.org/blog/columnar-storage/">Columnar Storage</a> - 解释什么是列式存储，以及我们什么时候需要它。</li><li><a href="http://www.actian.com/">Actian Vector</a> - 面向列的分析数据库。</li><li><a href="http://db.lcs.mit.edu/projects/cstore/">C-Store</a> - 面向列的 DBMS。</li><li><a href="https://clickhouse.yandex/">ClickHouse</a> - 一个开源的列式数据库（DBMS），主要用于在线分析处理查询（OLAP）。</li><li><a href="http://eventql.io/">EventQL</a> - 为大规模事件收集和分析而构建的分布式、面向列的数据库。</li><li><a href="https://www.monetdb.org/">MonetDB</a> - 列式存储数据库。</li><li><a href="http://parquet.apache.org/">Parquet</a> - 灵感来自于 2010 年 Google 发表的 Dremel 论文，是一种列式存储格式，与语言、平台无关，并且不需要和任何一种数据处理框架绑定。</li><li><a href="https://pivotal.io/pivotal-greenplum">Pivotal Greenplum</a> - 为特定目的而构建的专用分析数据仓库，它提供了一个列式存储引擎和一个传统的基于行的引擎。</li><li><a href="https://www.vertica.com/">Vertica</a> - 设计用于管理大量快速增长的数据，提供非常快的查询性能。</li><li><a href="http://sqream.com/">SQream DB</a> - 以色列大数据公司开发的跑在 GPU 上的大数据数据库，设计用于分析和数据仓库，使用 ANSI-92 SQL，适用于 10TB 到 1PB 的数据集。</li><li><a href="https://cloud.google.com/bigquery/what-is-bigquery">Google BigQuery</a> - Google 推出的一项 Web 服务，该服务让开发者可以使用 Google 的架构来运行 SQL 语句对超级大的数据库进行操作。</li><li><a href="https://aws.amazon.com/redshift/">Amazon Redshift</a> - 一个支持 SQL 查询的、快速、可扩展的列式存储数据库，它支持 PB 级的数量查询，是适用于企业级的数据仓库。</li><li><a href="https://github.com/shunfei/indexr">IndexR</a> - 一个开源的大数据存储格式，于 2017 年 1 月初正式开源，旨在通过添加索引、优化编码方式、提高 IO 效率等各种优化方式来提高计算层和存储层的数据交换效率，从而提升整体性能。</li><li><a href="https://github.com/cswinter/LocustDB">LocustDB</a> - 一个大规模并行且高性能的分析数据库 (Analytics Database)，可快速处理你的所有数据，目前处于实验性阶段。</li></ul><h2 id="NewSQL-数据库"><a href="#NewSQL-数据库" class="headerlink" title="NewSQL 数据库"></a>NewSQL 数据库</h2><ul><li><a href="http://www.actian.com/products/operational-databases/">Actian Ingres</a> - 商业支持，开源 SQL 关系数据库管理系统。</li><li><a href="https://github.com/biokoda/actordb">ActorDB</a> - 分布式的 SQL 数据库，可实现可伸缩的 K/V 存储系统。ActorDB 基于 Actor 计算模型，与传统的集中式数据库不同，ActorDB 由任意数量的被成为 actor 的独立和并发 SQL 数据库组成。</li><li><a href="http://aws.amazon.com/redshift/">Amazon RedShift</a> - 基于 PostgreSQL 的数据仓库服务。</li><li><a href="https://github.com/probcomp/BayesDB">BayesDB</a> - 一个贝叶斯数据库，内建贝叶斯查询语言 BQL，用户无需统计方面知识即可解决一些基本的科学数据问题</li><li><a href="http://bedrockdb.com/">Bedrock</a> - 构建在 SQLite 之上的简单、模块化、网络化、分布式事务层。</li><li><a href="https://www.citusdata.com/">CitusDB</a> - 通过分片和副本扩展 PostgreSQL。</li><li><a href="https://github.com/cockroachdb/cockroach">Cockroach</a> - 可伸缩、地理复制、事务性数据存储。</li><li><a href="https://github.com/bloomberg/comdb2">Comdb2</a> - 一个基于乐观并发控制技术的集群 RDBMS。</li><li><a href="http://www.datomic.com/">Datomic</a> - 分布式数据库旨在支持可伸缩、灵活和智能的应用程序。</li><li><a href="https://foundationdb.com/">FoundationDB</a> - 分布式数据库，受 F1 启发。</li><li><a href="https://research.google.com/pubs/pub41344.html">Google F1</a> - 构建在 Spanner 之上的分布式 SQL 数据库。</li><li><a href="https://research.google.com/archive/spanner.html">Google Spanner</a> - Google 的全球级的分布式数据库，具有可扩展，多版本，全球分布式、同步复制等特性。</li><li><a href="http://hstore.cs.brown.edu/">H-Store</a> - 一个实验性的数据库管理系统。它专为驻线交易处理应用程序（OLTP）而设计。</li><li><a href="https://github.com/VCNC/haeinsa">Haeinsa</a> - Haeinsa 是 HBase 可线性扩展的多行，多表事务库。使用两阶段锁定和乐观并发控制来实现事务。 事务的隔离级别是可序列化的。基于 Percolator 实现。</li><li><a href="https://www.percona.com/doc/percona-server/5.5/performance/handlersocket.html">HandlerSocket</a> - MySQL/MariaDB 的 NoSQL 插件。</li><li><a href="http://www.infinisql.org/">InfiniSQL</a> - 无限扩展的 RDBMS.</li><li><a href="https://github.com/rayokota/kareldb">KarelDB</a> - 由 Apache Kafka 支持的关系数据库。</li><li><a href="https://www.mapd.com/">Map-D</a> - GPU 内存数据库，大数据分析可视化平台。</li><li><a href="http://www.memsql.com/">MemSQL</a> - 一款内存数据库，它通过将数据存在内存中，将 SQL 语句预编译为 C++ 而获得极速执行效率。</li><li><a href="http://www.nuodb.com/">NuoDB</a> - 符合 SQL/ACID 的分布式数据库。</li><li><a href="http://www.oracle.com/technetwork/database/database-technologies/timesten/overview/index.html">Oracle TimesTen in-Memory Database</a> - 基于内存的关系数据库管理系统，具有持久性和可恢复性。</li><li><a href="http://gemfirexd.docs.pivotal.io/latest/">Pivotal GemFire XD</a> - 低延迟、基于内存、分布式 SQL 数据存储。为内存表数据提供 SQL 接口，可在 HDFS 中持久存储。</li><li><a href="https://hana.sap.com/abouthana.html">SAP HANA</a> - 基于内存、面向列、关系数据库管理系统。</li><li><a href="http://senseidb.github.io/sensei/">SenseiDB</a> - 分布式、实时、半结构化的数据库。</li><li><a href="http://skydb.io/">Sky</a> - 用于灵活、高性能的行为数据分析的数据库。</li><li><a href="http://www.symmetricds.org/">SymmetricDS</a> - 用于文件和数据库同步的开源软件。</li><li><a href="https://github.com/pingcap/tidb">TiDB</a> - 一款定位于在线事务处理/在线分析处理的融合型数据库产品，实现了一键水平伸缩，强一致性的多副本数据安全，分布式事务，实时 OLAP 等重要特性。受 Google F1 启发。</li><li><a href="https://www.voltdb.com/">VoltDB</a> - 声称是最快的内存数据库。</li><li><a href="https://github.com/YugaByte/yugabyte-db">yugabyteDB</a> - 与 PostgreSQL 兼容的开源，高性能，分布式 SQL 数据库。</li></ul><h2 id="时间序列数据库"><a href="#时间序列数据库" class="headerlink" title="时间序列数据库"></a>时间序列数据库</h2><ul><li><a href="http://axibase.com/products/axibase-time-series-database/">Axibase Time Series Database</a> - 基于 HBase 的时间序列数据库，内置可视化、规则引擎和 SQL 支持。</li><li><a href="http://chronix.io/">Chronix</a> - 一种时间序列存储器，用于存储高度压缩的时间序列，并支持快速访问数据。</li><li><a href="http://square.github.io/cube/">Cube</a> - 使用 MongoDB 来存储时间序列数据。</li><li><a href="https://spotify.github.io/heroic/#!/index">Heroic</a> - 基于 Cassandra 和 Elasticsearch 的可扩展时间序列数据库。</li><li><a href="https://www.influxdata.com/">InfluxDB</a> - 分布式时间序列数据库。</li><li><a href="https://www.circonus.com/irondb/">IronDB</a> - 可扩展、通用时间序列数据库。</li><li><a href="https://github.com/kairosdb/kairosdb">Kairosdb</a> - 和 OpenTSDB 类似，但是构建在 Cassandra 之上。</li><li><a href="http://m3db.github.io/m3/m3db/">M3DB</a> - 一个分布式时间序列数据库，可用于长期存储实时指标。</li><li><a href="https://opennms.github.io/newts/">Newts</a> - 基于 Apache Cassandra 的时间序列数据库。</li><li><a href="https://github.com/taosdata/TDengine/">TDengine</a> - 使用 IoT 的独特功能的 C 语言中的时间序列数据库，以提高读 / 写吞吐量并减少存储数据所需的空间。</li><li><a href="http://opentsdb.net">OpenTSDB</a> - 构建在 HBase 之上的分布式时间序列数据库。</li><li><a href="https://prometheus.io/">Prometheus</a> - 时间序列数据库和服务监控系统。</li><li><a href="https://github.com/facebookincubator/beringei">Beringei</a> - Facebook 的内存时间序列数据库。</li><li><a href="http://traildb.io/">TrailDB</a> - 用于存储和查询一系列事件的有效工具。</li><li><a href="https://github.com/druid-io/druid/">Druid</a> - MetaMarket 公司研发，专为海量数据集上的做高性能 OLAP (OnLine Analysis Processing)而设计的数据存储和分析系统</li><li><a href="http://basho.com/products/riak-ts/">Riak-TS</a> - 唯一专为物联网和时间序列数据优化的企业级 NoSQL 时间序列数据库。</li><li><a href="https://github.com/akumuli/Akumuli">Akumuli</a> - 一个数值型时间序列数据库，可以存储、处理时序列数据。</li><li><a href="https://github.com/Pardot/Rhombus">Rhombus</a> - ACassandra 的时间序列对象存储。</li><li><a href="https://github.com/dalmatinerdb/dalmatinerdb">Dalmatiner DB</a> - 快速分布式度量数据库</li><li><a href="https://github.com/rackerlabs/blueflood">Blueflood</a> - 一种用于摄取和处理时间序列数据的分布式系统。</li><li><a href="https://github.com/NationalSecurityAgency/timely">Timely</a> - 一个时间序列数据库应用程序，它提供了基于 Accumulo 和 Grafana 的对时间序列数据的安全访问。</li><li><a href="https://github.com/transceptor-technology/siridb-server">SiriDB</a> - 具有集群功能的高扩展性、健壮性和快速的开源时间序列数据库。</li><li><a href="https://github.com/improbable-eng/thanos">Thanos</a> - 一组组件，可以使用多个 Prometheus 部署创建具有无限存储容量的高可用度量系统。</li><li><a href="https://github.com/VictoriaMetrics/VictoriaMetrics">VictoriaMetrics</a> - 与 Prometheus 兼容的快速，可扩展的开源 TSDB，包括单节点和群集版本。</li></ul><h2 id="类-SQL-处理系统"><a href="#类-SQL-处理系统" class="headerlink" title="类 SQL 处理系统"></a>类 SQL 处理系统</h2><ul><li><a href="http://www.actian.com/analytic-database/vectorh-sql-hadoop">Actian SQL for Hadoop</a> - 高性能交互式 SQL，可以利用它访问 Hadoop 上的数据。</li><li><a href="http://drill.apache.org/">Apache Drill</a> - 一个低延迟的分布式海量数据交互式查询引擎，使用 ANSI SQL 兼容语法，本质上是一个分布式的 MPP 查询层。目的在于支持更广泛的数据源，数据格式，以及查询语言。受 Google 的 Dremel 启发。</li><li><a href="https://cwiki.apache.org/confluence/display/Hive/HCatalog">Apache HCatalog</a> - Hadoop 的表存储管理工具。</li><li><a href="http://hive.apache.org/">Apache Hive</a> - 基于 Hadoop 的一个数据仓库工具，可以将结构化数据文件映射为一张数据库表，并提供类 SQL 查询功能。</li><li><a href="http://calcite.apache.org/">Apache Calcite</a> - 一款开源 SQL 解析工具，可以将各种 SQL 语句解析成抽象语法术 AST(Abstract Syntax Tree)，之后通过操作 AST 就可以把 SQL 中所要表达的算法与关系体现在具体代码之中。</li><li><a href="http://phoenix.apache.org/index.html">Apache Phoenix</a> - 构建在 HBase 之上的关系型数据库层，可以对 HBase 中的数据进行低延迟访问。</li><li><a href="http://www.teradata.com/products-and-services/Teradata-Aster/teradata-aster-database">Aster Database</a> - 类 SQL 分析处理。</li><li><a href="https://www.cloudera.com/products/apache-hadoop/impala.html">Cloudera Impala</a> - 实时交互 SQL 大数据查询工具，受 Dremel 启发。</li><li><a href="http://www.cascading.org/projects/lingual/">Concurrent Lingual</a> - Cascading 上的 SQL 查询语言。</li><li><a href="http://www.datasalt.com/products/splout-sql/">Datasalt Splout SQL</a> - 针对大数据集的完整 SQL 查询引擎。</li><li><a href="https://www.dremio.com/">Dremio</a> - 一个基于 Apache Arrow 的开源，类似 SQL 的数据服务平台。</li><li><a href="https://prestodb.io/">Facebook PrestoDB</a> - 分布式 SQL 查询引擎。</li><li><a href="https://research.google.com/pubs/pub36632.html">Google BigQuery</a> - Google 推出的一项 Web 服务，该服务让开发者可以使用 Google 的架构来运行 SQL 语句对超级大的数据库进行操作，是 Dremel 的实现。</li><li><a href="https://www.pipelinedb.com/">PipelineDB</a> - 一个开源的关系数据库，它可以在实时流数据上执行 SQL 查询，并将结果增量地存储在表中。</li><li><a href="https://pivotal.io/pivotal-hdb">Pivotal HDB</a> - Hadoop 上的类 SQL 数据仓库系统。</li><li><a href="http://rainstor.com/products/rainstor-database/">RainstorDB</a> - 用于存储 PB 级结构化和半结构化数据量的数据库。</li><li><a href="https://github.com/apache/spark/tree/master/sql">Spark Catalyst</a> - Apache Spark 的查询优化框架。</li><li><a href="https://databricks.com/blog/2014/03/26/spark-sql-manipulating-structured-data-using-spark-2.html">SparkSQL</a> - 使用 Spark 操作结构化的数据。</li><li><a href="https://www.splicemachine.com/">Splice Machine</a> - 兼具了 SQL 和 NoSQL 的各自优势，且能对操作型和分析型应用进行实时处理，具有 ACID 特性。</li><li><a href="https://hortonworks.com/innovation/stinger/">Stinger</a> - 由 Hortonworks 开发的一个彻底提升 Hive 效率的工具。</li><li><a href="http://tajo.apache.org/">Tajo</a> - Hadoop 之上的分布式数据仓库系统。</li><li><a href="https://wiki.trafodion.org/wiki/index.php/Main_Page">Trafodion</a> - 由惠普开发并开源的基于 Hadoop 平台的事务数据库引擎。提供了一个基于 Hadoop 平台的交易型 SQL 引擎，是一个擅长处理交易型负载的 Hadoop 大数据解决方案。</li></ul><h2 id="数据采集"><a href="#数据采集" class="headerlink" title="数据采集"></a>数据采集</h2><ul><li><a href="https://aws.amazon.com/kinesis/">Amazon Kinesis</a> - 一种在 AWS 上流式处理数据的平台，让您可以轻松地加载和分析流数据，同时还可让您根据具体需求来构建自定义流数据应用程序。</li><li><a href="https://aws.amazon.com/glue/">Amazon Web Services Glue</a> -  一项完全托管的提取、转换和加载（ETL）服务，让用户能够轻松准备和加载数据进行分析。</li><li><a href="http://chukwa.apache.org/">Apache Chukwa</a> - 数据采集系统。</li><li><a href="http://flume.apache.org/">Apache Flume</a> - 一个分布式的、可靠的、易用的系统，可以有效地将来自很多不同源系统的大量日志数据收集、汇总或者转移到一个数据中心存储。</li><li><a href="http://kafka.apache.org/">Apache Kafka</a> - 分布式发布订阅消息系统。</li><li><a href="https://nifi.apache.org/">Apache NiFi</a> - 一个易用、强大、可靠的数据处理与分发系统</li><li><a href="http://sqoop.apache.org/">Apache Sqoop</a> - 一款开源的工具，主要用于在 Hadoop/Hive 与传统的数据库（MySQL、Oracle...）间进行数据的传递</li><li><a href="http://www.embulk.org">Embulk</a> - 开源的批量数据加载器，帮助在各种数据库、存储、文件格式和云服务之间传输数据。</li><li><a href="https://github.com/facebookarchive/scribe">Facebook Scribe</a> - 流日志数据聚合器。</li><li><a href="http://www.fluentd.org">Fluentd</a> - 用于收集事件和日志的工具。</li><li><a href="https://research.google.com/pubs/pub41318.html">Google Photon</a> - 地理分布式系统，用于实时连接多个连续流动的数据流，具有高可伸缩性和低延迟。</li><li><a href="https://github.com/mozilla-services/heka">Heka</a> - 开源流处理系统。</li><li><a href="https://github.com/sonalgoyal/hiho">HIHO</a> - 用于将不同数据源的数据和 Hadoop 进行连接的框架。</li><li><a href="https://github.com/papertrail/kestrel">Kestrel</a> - 分布式消息队列系统。</li><li><a href="https://engineering.linkedin.com/data">LinkedIn Databus</a> - LinkedIn 开源的一个低延迟、可靠的、支持事务的、保持一致性的数据变更抓取系统。</li><li><a href="https://github.com/linkedin/kamikaze">LinkedIn Kamikaze</a> - 一种实用工具包，对 document lists 提供一系列的实现。</li><li><a href="https://github.com/linkedin/white-elephant">LinkedIn White Elephant</a> - 一个 Hadoop 日志收集器和展示器，它提供了用户角度的 Hadoop 集群可视化。</li><li><a href="https://www.elastic.co/products/logstash">Logstash</a> - 一个开源的日志收集管理工具，可以采集来自不同数据源的数据，并对数据进行处理后输出到多种输出源。</li><li><a href="https://github.com/Netflix/suro">Netflix Suro</a> - Netflix 开源的一款工具，它能够在数据被发送到不同的数据平台（如 Hadoop、Elasticsearch）之前，收集不同应用服务器上的事件数据。</li><li><a href="https://github.com/pinterest/secor">Pinterest Secor</a> - 实现 Kafka 日志持久性的服务</li><li><a href="https://github.com/linkedin/gobblin">Linkedin Gobblin</a> - 一套分布式数据集成框架，旨在简化大数据集成工作当中的各类常见任务，具体包括数据流与批量生态系统的提取、复制、组织与生命周期管理。</li><li><a href="https://github.com/skizzehq/skizze">Skizze</a> - 一种概率数据结构服务和存储。</li><li><a href="https://github.com/streamsets/datacollector">StreamSets Data Collector</a> - 使用一个简单的 IDE 来连续大数据摄取基础设施。</li><li><a href="https://github.com/apache/incubator-pulsar">Yahoo Pulsar</a> - 由 Yahoo 开发并开源的一个企业级的发布订阅消息系统。</li><li><a href="https://www.alooma.com/integrations/mysql">Alooma</a> - 实时的数据管道服务，支持将 MySQL 等数据源的数据移动到数据仓库中。</li></ul><h2 id="服务编程"><a href="#服务编程" class="headerlink" title="服务编程"></a>服务编程</h2><ul><li><a href="http://akka.io/">Akka Toolkit</a> - 基于 Actor 模型，提供了一个用于构建可扩展的（Scalable）、弹性的（Resilient）、快速响应的（Responsive）应用程序的平台。</li><li><a href="http://avro.apache.org/">Apache Avro</a> - 数据序列化系统。</li><li><a href="http://curator.apache.org/">Apache Curator</a> - 为 Apache ZooKeeper 开发的类库。</li><li><a href="http://karaf.apache.org/">Apache Karaf</a> - Apache 旗下的一个开源项目，同时也是一个基于 OSGi 的运行环境，Karaf 提供了一个轻量级的 OSGi 容器，可以用于部署各种组件，应用程序。</li><li><a href="http://thrift.apache.org//">Apache Thrift</a> - Facebook 开源的跨语言的 RPC 通信框架。</li><li><a href="http://zookeeper.apache.org/">Apache Zookeeper</a> - 一个分布式应用程序协调服务。</li><li><a href="https://research.google.com/archive/chubby.html">Google Chubby</a> - 一个分布式锁服务，Chubby 底层一致性实现就是以 Paxos 为基础的</li><li><a href="https://github.com/Hydrospheredata/mist">Hydrosphere Mist</a> - 一个将 Apache Spark 分析任务和机器学习模型转换为实时、批处理或反应性 web 服务的服务。</li><li><a href="https://engineering.linkedin.com/data">Linkedin Norbert</a> - 集群管理系统。</li><li><a href="https://github.com/mara/data-integration">Mara</a> - 一个轻量级的自定义 ETL 框架。</li><li><a href="https://www.open-mpi.org/">OpenMPI</a> - 消息传递框架。</li><li><a href="https://www.serf.io/">Serf</a> - 去中心化的服务发现和编排解决方案。</li><li><a href="https://github.com/spotify/luigi">Spotify Luigi</a> - 用于构建批处理作业的复杂管道的 Python 包。它处理依赖项解析、工作流管理、可视化、处理故障、命令行集成等等。</li><li><a href="https://github.com/spring-projects/spring-xd">Spring XD</a> - 用于数据摄取、实时分析、批处理和数据导出的分布式和可扩展系统。</li><li><a href="https://github.com/twitter/elephant-bird">Twitter Elephant Bird</a> - 用于处理 LZOP 压缩数据的库。</li><li><a href="https://twitter.github.io/finagle/">Twitter Finagle</a> - JVM 的异步网络堆栈。</li></ul><h2 id="调度"><a href="#调度" class="headerlink" title="调度"></a>调度</h2><ul><li><a href="https://github.com/apache/incubator-airflow">Apache Airflow</a> - Airbnb 开源的一个用 Python 编写的工作流管理平台。</li><li><a href="http://aurora.apache.org/">Apache Aurora</a> - 长期运行服务和计划作业的 Mesos 框架。</li><li><a href="http://falcon.apache.org/">Apache Falcon</a> - 数据管理框架。</li><li><a href="http://oozie.apache.org/">Apache Oozie</a> - 工作流作业调度器。</li><li><a href="https://docs.microsoft.com/en-us/azure/data-factory/data-factory-introduction">Azure Data Factory</a> - 可大规模简化 ETL 的混合数据集成服务</li><li><a href="http://mesos.github.io/chronos/">Chronos</a> - 分布式和容错调度器。</li><li><a href="https://azkaban.github.io/">Linkedin Azkaban</a> - 批处理工作流作业调度程序。</li><li><a href="https://github.com/ottogroup/schedoscope">Schedoscope</a> - 用于 Hadoop 作业的敏捷调度 Scala DSL。</li><li><a href="https://github.com/radlab/sparrow">Sparrow</a> - 调度平台。</li></ul><h2 id="机器学习"><a href="#机器学习" class="headerlink" title="机器学习"></a>机器学习</h2><ul><li><a href="https://studio.azureml.net/">Azure ML Studio</a> - 基于云的 R、Python 机器学习平台。</li><li><a href="https://github.com/harthur/brain">brain</a> - JavaScript 中的神经网络。</li><li><a href="https://github.com/OryxProject/oryx">Oryx</a> - 实时大规模机器学习。</li><li><a href="http://www.cascading.org/projects/pattern/">Concurrent Pattern</a> - Cascading 上的机器学习框架。</li><li><a href="https://github.com/karpathy/convnetjs">convnetjs</a> - Javascript 中的深入学习，可以在浏览器中训练卷积神经网络（或普通神经网络）。</li><li><a href="https://github.com/deeplearning4j/DataVec">DataVec</a> - 一个用于 Java 和 Scala 深度学习的矢量化和数据预处理库。Deeplearning4j 生态系统的一部分。</li><li><a href="https://github.com/deeplearning4j">Deeplearning4j</a> - 美国 AI 创业公司 Skymind 开源并维护的一个基于 Java/JVM 的深度学习框架，可使用 CPU 或 GPU 运行。</li><li><a href="https://github.com/danielsdeleo/Decider">Decider</a> - Ruby 中灵活且可扩展的机器学习。</li><li><a href="http://www.heatonresearch.com/encog/">ENCOG</a> - 支持多种高级算法的机器学习框架，以及支持规范化和处理数据的类。</li><li><a href="http://www.etcml.com/">etcML</a> - 在线免费文本分析工具是由美国的斯坦福大学计算机教授开发的基于成熟的文本分析引擎</li><li><a href="https://github.com/etsy/Conjecture">Etsy Conjecture</a> - Scalding 中可扩展的机器学习。</li><li><a href="https://github.com/gojek/feast">Feast</a> - 用于管理、发现和访问机器学习特性的特性存储库。Feast 为模型训练和模型服务提供了一致的特征数据视图。</li><li><a href="https://dato.com/products/create/">GraphLab Create</a> - Python 中的机器学习平台，包含大量 ML 工具包、数据工程和部署工具。</li><li><a href="https://github.com/h2oai/h2o-3/">H2O</a> - 使用 Hadoop、R 和 Python 进行统计、机器学习和数学运行时。</li><li><a href="https://github.com/benedekrozemberczki/karateclub">Karate Club</a> - 用于图形结构化数据的 Python 无监督机器学习库。</li><li><a href="https://github.com/fchollet/keras">Keras</a> - 一个高层神经网络 API，Keras 由纯 Python 编写而成并基 Tensorflow、Theano 以及 CNTK 后端。受 Torch 启发。</li><li><a href="https://github.com/johnsonc/lambdo">Lambdo</a> - 是一个工作流引擎，通过将一个分析管道、特征工程和机器学习、模型训练和预测结合起来，通过用户定义（Python）函数实现表填充和列评估，大大简化了数据处理和分析。</li><li><a href="http://mahout.apache.org/">Mahout</a> - 是 Apache Software Foundation（ASF）旗下的一个开源项目，提供一些可扩展的机器学习领域经典算法的实现，旨在帮助开发人员更加方便快捷地创建智能应用程序。</li><li><a href="http://www.mlbase.org/">MLbase</a> - 是 Spark 生态圈的一部分，专注于机器学习，包含三个组件：MLlib、MLI、ML Optimizer。</li><li><a href="https://github.com/nikolaypavlov/MLPNeuralNet">MLPNeuralNet</a> - 一个针对 iOS 和 Mac OS 系统的快速多层感知神经网络库，可通过已训练的神经网络预测新实例。</li><li><a href="https://github.com/ml-tooling/ml-workspace">ML Workspace</a> - 基于 Web 的多合一 IDE，专门用于机器学习和数据科学。</li><li><a href="http://moa.cms.waikato.ac.nz">MOA</a> - 实时进行大数据流挖掘和大规模机器学习。</li><li><a href="https://monkeylearn.com/">MonkeyLearn</a> - 让文本挖掘变得很容易，可以从文本中提取和分类数据。</li><li><a href="https://github.com/deeplearning4j/nd4j">ND4J</a> - JVM 的矩阵库，可以认为是 Java 中的 Numpy。</li><li><a href="https://github.com/numenta/nupic">nupic</a> - 一个实现了 HTM 学习算法的机器智能平台。</li><li><a href="http://predictionio.incubator.apache.org/index.html">PredictionIO</a> - 面向开发人员和数据科学家的开源机器学习服务，构建在 Hadoop，Mahout 和 Cascading 之上。</li><li><a href="https://github.com/deeplearning4j/rl4j">RL4J</a> - 一个与 Deeplearning4j 集成的强化学习框架。</li><li><a href="http://samoa.incubator.apache.org/">SAMOA</a> - 分布式流数据机器学习框架。</li><li><a href="https://github.com/scikit-learn/scikit-learn">scikit-learn</a> - 专门面向机器学习的 Python 开源框架，实现了各种成熟的算法。</li><li><a href="http://spark.apache.org/docs/0.9.0/mllib-guide.html">Spark MLlib</a> - 使用 Spark 实现一些常见的机器学习算法和实用程序，包括分类、回归、聚类、协同过滤、降维以及底层优化。</li><li><a href="https://users.soe.ucsc.edu/~niejiazhong/slides/chandra.pdf">Sibyl</a> - 谷歌大型机器学习系统。</li><li><a href="https://github.com/tensorflow/tensorflow">TensorFlow</a> - 一个采用数据流图（data flow graphs），用于数值计算的开源软件库。</li><li><a href="https://github.com/theano">Theano</a> - 蒙特利尔大学支持的以 Python 为核心的机器学习类库。</li><li><a href="https://github.com/torch">Torch</a> - 一个基于 BSD License 的开源的机器学习的框架。</li><li><a href="https://github.com/amplab/velox-modelserver">Velox</a> - 服务于机器学习预测的系统。</li><li><a href="https://github.com/JohnLangford/vowpal_wabbit/wiki">Vowpal Wabbit</a> - 由微软和雅虎赞助的学习系统。</li><li><a href="http://www.cs.waikato.ac.nz/ml/weka/">WEKA</a> - 一套机器学习软件。</li><li><a href="https://github.com/BIDData/BIDMach">BidMach</a> - CPU 和 GPU 加速库的机器学习库。</li></ul><h2 id="基准"><a href="#基准" class="headerlink" title="基准"></a>基准</h2><ul><li><a href="https://issues.apache.org/jira/browse/MAPREDUCE-3561">Apache Hadoop Benchmarking</a> - 测试 Hadoop 性能的微基准测试。</li><li><a href="https://github.com/SWIMProjectUCB/SWIM/wiki">Berkeley SWIM Benchmark</a> - 真实大数据工作负载基准。</li><li><a href="https://github.com/intel-hadoop/HiBench">Intel HiBench</a> - Hadoop 基准套件。</li><li><a href="https://issues.apache.org/jira/browse/MAPREDUCE-5116">PUMA Benchmarking</a> - MapReduce 应用程序的基准测试套件。</li><li><a href="http://yahoohadoop.tumblr.com/post/98294079296/gridmix3-emulating-production-workload-for">Yahoo Gridmix3</a> - 来自 Yahoo 工程师团队的 Hadoop 集群基准测试。</li><li><a href="https://github.com/deeplearning4j/dl4j-benchmark">Deeplearning4j Benchmarks</a> - 对流行的模型和配置进行基准测试，并输出性能和版本统计信息。</li></ul><h2 id="安全"><a href="#安全" class="headerlink" title="安全"></a>安全</h2><ul><li><a href="http://ranger.apache.org/">Apache Ranger</a> - 一个用在 Hadoop 平台上并提供操作、监控、管理综合数据安全的框架。</li><li><a href="http://eagle.apache.org/">Apache Eagle</a> - 由 eBay 公司开源的一个识别大数据平台上的安全和性能问题的开源解决方案。</li><li><a href="http://knox.apache.org/">Apache Knox Gateway</a> - Hadoop 集群中用于数据处理的 REST API 网关。</li><li><a href="http://incubator.apache.org/projects/sentry.html">Apache Sentry</a> - 为 Hadoop 集群中的元数据和数据存储提供集中、细粒度的访问控制。</li><li><a href="https://github.com/kotobukki/BDA/">BDA</a> - Hadoop 和 Spark 的漏洞检测器。</li></ul><h2 id="系统部署"><a href="#系统部署" class="headerlink" title="系统部署"></a>系统部署</h2><ul><li><a href="http://ambari.apache.org/">Apache Ambari</a> - 一个集中部署、管理、监控 Hadoop 分布式集群的工具。</li><li><a href="http://bigtop.apache.org//">Apache Bigtop</a> - 一个针对基础设施工程师和数据科学家的开源项目，旨在全面打包、测试和配置领先的开源大数据组件 / 项目，包括但不限于 Hadoop、HBase 和 Spark 。</li><li><a href="http://helix.apache.org/">Apache Helix</a> - 集群管理框架。</li><li><a href="http://mesos.apache.org/">Apache Mesos</a> - 一个类似于 YARN 的集群管理器，提供了有效的、跨分布式应用或框架的资源隔离和共享，可以运行 Hadoop、MPI、Hypertable、Spark。</li><li><a href="https://github.com/apache/incubator-slider">Apache Slider</a> - 是一个 YARN 应用程序，用于在 YARN 上部署现有的分布式应用程序。</li><li><a href="http://whirr.apache.org/">Apache Whirr</a> - 运行云服务的一组 Java 类库。</li><li><a href="https://hortonworks.com/hadoop/yarn/">Apache YARN</a> - 集群管理系统。</li><li><a href="http://brooklyncentral.github.io/">Brooklyn</a> - 简化应用程序部署和管理的库。</li><li><a href="http://buildoop.github.io/">Buildoop</a> - 类似于 Apache BigTop，基于 Groovy 语言开发。</li><li><a href="http://gethue.com/">Cloudera HUE</a> - 用于与 Hadoop 交互的 Web 应用程序。</li><li><a href="http://www.wired.com/2012/08/facebook-prism/">Facebook Prism</a> - 多数据中心复制系统。</li><li><a href="https://www.wired.com/2013/03/google-borg-twitter-mesos/all/">Google Borg</a> - Google 的内部大型集群管理系统。</li><li><a href="https://www.youtube.com/watch?v=0ZFMlO98Jkc">Google Omega</a> - Google 内部第三代的集群管理框架。</li><li><a href="https://hortonworks.com/blog/introducing-hoya-hbase-on-yarn/">Hortonworks HOYA</a> - 可以在 YARN 上部署 HBase 集群的应用程序。</li><li><a href="https://kubernetes.io/">Kubernetes</a> - Google 团队发起并维护的基于 Docker 的开源容器集群管理系统。</li><li><a href="https://github.com/mesosphere/marathon">Marathon</a> - 一个 Mesos 框架，能够支持运行长服务。</li></ul><h2 id="应用程序"><a href="#应用程序" class="headerlink" title="应用程序"></a>应用程序</h2><ul><li><a href="https://github.com/etsy/411">411</a> - 一个 Web 应用程序，用于通过计划搜索 Elasticsearch 而产生的警报管理。</li><li><a href="https://github.com/adobe-research/spindle">Adobe spindle</a> - 使用 Scala、Spark 和 Parquet 进行 web 分析的下一代系统。</li><li><a href="http://metron.apache.org/">Apache Metron</a> - 一个集成了各种开源大数据技术的平台，以提供用于安全监视和分析的集中式工具。</li><li><a href="http://nutch.apache.org/">Apache Nutch</a> - 开源 web 爬虫程序</li><li><a href="http://oodt.apache.org/">Apache OODT</a> - NASA 开源的用于做数据管理的系统。</li><li><a href="https://tika.apache.org/">Apache Tika</a> - 使用 Java 编写的内容检测和分析框架。</li><li><a href="https://github.com/salesforce/Argus">Argus</a> - 时序监控报警平台。</li><li><a href="https://github.com/uber/AthenaX">AthenaX</a> - 一个流分析平台，允许用户使用结构化查询语言（SQL）运行生产质量的大规模流分析。</li><li><a href="https://github.com/Netflix/atlas">Atlas</a> - 用于管理维度时间序列数据的系统。</li><li><a href="https://count.ly/">Countly</a> - 基于 Node.js 和 MongoDB 的开源移动和 web 分析平台。</li><li><a href="https://www.dominodatalab.com/">Domino</a> - 运行、扩展、共享和部署模型，不需要任何基础设施。</li><li><a href="http://www.eclipse.org/birt/">Eclipse BIRT</a> - 基于 Elasticsearch 的报告系统。</li><li><a href="https://github.com/Yelp/elastalert">ElastAert</a> - 为 ES 打造的报警监控工具。</li><li><a href="https://github.com/Codecademy/EventHub">Eventhub</a> - 开源事件分析平台。</li><li><a href="https://github.com/allegro/hermes">Hermes</a> - 构建在 Kafka 之上的异步消息代理。</li><li><a href="http://hipi.cs.virginia.edu/">HIPI Library</a> - 使用 Hadoop 的 MapReduce 来执行图像处理任务的 API。</li><li><a href="https://www.splunk.com/en_us/download/hunk.html">Hunk</a> - Hadoop 的分析工具。</li><li><a href="http://opensource.indeedeng.io/imhotep/">Imhotep</a> - 大型分析平台。</li><li><a href="https://www.indicative.com/">Indicative</a> - Web 和移动分析工具，具有数据仓库（AWS，BigQuery）集成。</li><li><a href="https://jupyter.org/">Jupyter</a> - 基于网页的用于交互计算的应用程序。其可被应用于全过程计算：开发、文档编写、运行代码和展示结果。</li><li><a href="http://madlib.incubator.apache.org/community/">MADlib</a> - RDBMS 的数据处理库，用于分析数据。</li><li><a href="https://github.com/influxdata/kapacitor">Kapacitor</a> - 用于对时间序列数据进行处理、监视和警报的开源框架。</li><li><a href="http://kylin.apache.org/">Kylin</a> - 一个开源的分布式分析引擎，提供 Hadoop/Spark 之上的 SQL 查询接口及多维分析（OLAP）能力以支持超大规模数据，最初由 eBay Inc. 开发并贡献至开源社区，能在亚秒内查询巨大的 Hive 表。</li><li><a href="https://github.com/pivotalsoftware/PivotalR">PivotalR</a> - 支持在 Pivotal HD/HAWQ 以及 PostgreSQL 上运行 R。</li><li><a href="https://github.com/rakam-io/rakam">Rakam</a> - 开源实时自定义分析平台，由 PostgreSQL，Kinesis 和 PrestoDB 提供支持。</li><li><a href="https://www.qubole.com/">Qubole</a> - 能够自动扩展 Hadoop 集群以及内置的链接器。</li><li><a href="https://sense.io/">Sense</a> - 数据科学和大数据分析的云平台。</li><li><a href="https://github.com/SnappyDataInc/snappydata">SnappyData</a> - 一个统一 OLTP + OLAP + 流式写入 的内存分布式数据库。</li><li><a href="https://github.com/snowplow/snowplow">Snowplow</a> - 由 Hadoop，Kinesis，Redshift 和 Postgres 支持的企业级 Web 和事件分析。</li><li><a href="http://amplab-extras.github.io/SparkR-pkg/">SparkR</a> - 用于 Spark 的 R 前端。</li><li><a href="https://www.splunk.com/">Splunk</a> - 一款成熟的商业化日志处理分析产品。</li><li><a href="https://www.sumologic.com/">Sumo Logic</a> - 基于云的日志处理分析产品。</li><li><a href="http://www.talend.com/products/big-data/">Talend</a> - YARN、Hadoop、HBASE、Hive、HCatalog 和 Pig 的统一开源环境。</li><li><a href="https://warp.one//">Warp</a> - 大数据示例查询工具（OS X 应用）。</li></ul><h2 id="搜索引擎和框架"><a href="#搜索引擎和框架" class="headerlink" title="搜索引擎和框架"></a>搜索引擎和框架</h2><ul><li><a href="http://lucene.apache.org/">Apache Lucene</a> - 一套用于全文检索和搜索的开放源码程序库。</li><li><a href="http://lucene.apache.org/solr/">Apache Solr</a> - 是 Apache Lucene 项目的开源企业搜索平台。其主要功能包括全文检索、命中标示、分面搜索、动态聚类、数据库集成，以及富文本（如 Word、PDF）的处理。</li><li><a href="https://github.com/strapdata/elassandra">Elassandra</a> - 是 Elasticsearch 的一个分支，经过修改，可以作为 Apache Cassandra 的插件运行，具有可扩展和灵活的点对点架构。</li><li><a href="https://www.elastic.co/">Elasticsearch</a> - 一个基于 Lucene 库的搜索引擎。它提供了一个分布式、支持多租户的全文搜索引擎，具有 HTTP Web 接口和无模式 JSON 文档。</li><li><a href="https://www.enigma.com/">Enigma.io</a> – 免费增值的 Web 应用程序，用于对 Web 上抓取的海量数据集进行浏览，过滤，分析，搜索和导出。</li><li><a href="https://www.facebook.com/publications/219621248185635/">Facebook Unicorn</a> - 社交图搜索平台。</li><li><a href="https://googleblog.blogspot.it/2010/06/our-new-search-index-caffeine.html">Google Caffeine</a> - 一个高性能、出色的缓存类库</li><li><a href="https://research.google.com/pubs/pub36726.html">Google Percolator</a> - 由 Google 公司开发的、为大数据集群进行增量处理更新的系统，主要用于 google 网页搜索索引服务。</li><li><a href="https://blogs.apache.org/hbase/entry/coprocessor_introduction">HBase Coprocessor</a> - HBase 的协处理器，Percolator 的实现。</li><li><a href="http://ngdata.github.io/hbase-indexer/">Lily HBase Indexer</a> - 一款快速、简单的 HBase 的内容检索方案，它可以帮助你在 Solr 中建立 HBase 的数据索引，从而通过 Solr 进行数据检索。</li><li><a href="http://senseidb.github.io/bobo/">LinkedIn Bobo</a> - 完全用 Java 编写的 Faceted Search 实现，是 Apache Lucene 的扩展。</li><li><a href="https://github.com/linkedin/cleo">LinkedIn Cleo</a> - 一个灵活的软件库，用于处理一些预输入和自动完成的搜索功能。</li><li><a href="https://engineering.linkedin.com/search/did-you-mean-galene">LinkedIn Galene</a> - LinkedIn 的搜索架构。</li><li><a href="https://github.com/senseidb/zoie">LinkedIn Zoie</a> - 一个用 Java 编写的实时搜索/索引系统。</li><li><a href="http://mg4j.di.unimi.it/">MG4J</a> - MG4J（Managing Gigabytes for Java）是一个用 Java 编写的大型文档集合的全文搜索引擎，它是高度可定制的，高性能的，并提供了最先进的功能和新的研究算法。</li><li><a href="http://sphinxsearch.com/">Sphinx Search Server</a> - 全文搜索引擎。</li><li><a href="http://vespa.ai/">Vespa</a> - 在大型数据集上进行低延迟计算的引擎。它存储和索引数据，以便可以在服务时执行对数据的查询，选择和处理</li><li><a href="https://github.com/facebookresearch/faiss">Facebook Faiss</a> - 用于高效相似性搜索和密集向量聚类的库。 它包含的算法可搜索任意大小的向量集，最多可搜索到不适合 RAM 的向量。 它还包含用于评估和参数调整的支持代码。Faiss 用 C++ 编写，带有完整的 Python/numpy 包装器。</li><li><a href="https://github.com/spotify/annoy">Annoy</a> - 具有 Python 绑定的 C++库，用于搜索空间中接近给定查询点的点。 它还会创建大型的基于文件的只读数据结构，这些数据结构被映射到内存中，以便许多进程可以共享相同的数据。</li></ul><h2 id="MySQL-分支和发展"><a href="#MySQL-分支和发展" class="headerlink" title="MySQL 分支和发展"></a>MySQL 分支和发展</h2><ul><li><a href="https://aws.amazon.com/rds/">Amazon RDS</a> - AWS 的 MySQL 数据库。</li><li><a href="http://www.drizzle.org/">Drizzle</a> - MySQL 6.0 的发展。</li><li><a href="https://cloud.google.com/sql/docs/">Google Cloud SQL</a> - Google 云中的 MySQL 数据库。</li><li><a href="https://mariadb.org/">MariaDB</a> - MySQL 的一个分支，采用 GPL 授权许可。目的是完全兼容 MySQL，包括 API 和命令行。</li><li><a href="https://www.mysql.com/products/cluster/">MySQL Cluster</a> - 使用 NDB 集群存储引擎实现 MySQL 集群。</li><li><a href="https://www.percona.com/software/mysql-database/percona-server">Percona Server</a> - MySQL 增强版，可以替代它。</li><li><a href="https://github.com/renecannao/proxysql">ProxySQL</a> - MySQL 的高性能代理。</li><li><a href="https://www.percona.com/">TokuDB</a> - MySQL 和 MariaDB 的存储引擎。</li><li><a href="http://webscalesql.org/">WebScaleSQL</a> - Facebook、 Google、Twitter 和 Linkedin 四家公司的 MySQL 团队发起的 MySQL 开源组织，旨在改进 MySQL 在规模和性能等方面的问题。</li></ul><h2 id="PostgreSQL-分支和发展"><a href="#PostgreSQL-分支和发展" class="headerlink" title="PostgreSQL 分支和发展"></a>PostgreSQL 分支和发展</h2><ul><li><a href="http://db.cs.yale.edu/hadoopdb/hadoopdb.html">HadoopDB</a> - MapReduce 和 DBMS 的混合体。</li><li><a href="http://www-01.ibm.com/software/data/netezza/">IBM Netezza</a> - 高性能数据仓库设备。</li><li><a href="http://www.postgres-xl.org/">Postgres-XL</a> - 可伸缩的基于 PostgreSQL 的开源数据库集群。</li><li><a href="http://www-users.cs.umn.edu/~sarwat/RecDB/">RecDB</a> - 完全在 PostgreSQL 内部构建的开源推荐引擎。</li><li><a href="http://www.stormdb.com/community/stado">Stado</a> - 仅针对数据仓库和数据集市应用程序的开源 MPP 数据库系统。</li><li><a href="https://www.scribd.com/doc/3159239/70-Everest-PGCon-RT">Yahoo Everest</a> - 由 PostgreSQL 派生的 PB 级数据库 / MPP。</li><li><a href="http://www.timescale.com/">TimescaleDB</a> - 针对快速摄取和复杂查询而优化的开源时间序列数据库。</li><li><a href="https://www.pipelinedb.com/">PipelineDB</a> - 开源的流式数据库，基于 PostgreSQL 数据库改造的，允许我们通过 SQL 的方式，对数据流做操作，并把操作结果储存起来。</li></ul><h2 id="Memcached-分支和发展"><a href="#Memcached-分支和发展" class="headerlink" title="Memcached 分支和发展"></a>Memcached 分支和发展</h2><ul><li><a href="https://www.facebook.com/notes/facebook-engineering/mcdipper-a-key-value-cache-for-flash-storage/10151347090423920">Facebook McDipper</a> - 用于闪存的 key/value 缓存，设计目的在于提高闪存存储的使用效率。</li><li><a href="https://www.facebook.com/notes/facebook-engineering/scaling-memcache-at-facebook/10151411410803920">Facebook Memcached</a> - Memcache 的分支。</li><li><a href="https://github.com/twitter/twemproxy">Twemproxy</a> - 一个快速、轻量级的 memcached 和 redis 代理。</li><li><a href="https://github.com/twitter/fatcache">Twitter Fatcache</a> - 用于闪存的 key/value 缓存。</li><li><a href="https://github.com/twitter/twemcache">Twitter Twemcache</a> - Memcache 的分支。</li></ul><h2 id="嵌入式数据库"><a href="#嵌入式数据库" class="headerlink" title="嵌入式数据库"></a>嵌入式数据库</h2><ul><li><a href="http://www.actian.com/products/operational-databases/">Actian PSQL</a> - 由 Pervasive Software 开发的符合 ACID 的 DBMS，针对嵌入应用程序进行了优化。</li><li><a href="https://www.oracle.com/database/berkeley-db/index.html">BerkeleyDB</a> - 可为 key/value 数据提供高性能的嵌入式数据库。</li><li><a href="https://github.com/krestenkrab/hanoidb">HanoiDB</a> - Erlang LSM BTree 存储。</li><li><a href="https://github.com/google/leveldb">LevelDB</a> - Google 开源的持久化 KV 单机数据库，具有很高的随机写，顺序读/写性能。</li><li><a href="https://symas.com/mdb/">LMDB</a> - 由 Symas 开发的基于 Btree-based 的高性能 mmap key-value 数据库。</li><li><a href="http://rocksdb.org/">RocksDB</a> - Facebook 公司基于 LevelDB 开发的一款开源嵌入式数据库引擎。</li></ul><h2 id="商业智能"><a href="#商业智能" class="headerlink" title="商业智能"></a>商业智能</h2><ul><li><a href="https://www.bimeanalytics.com/?lang=en">BIME Analytics</a> - 商业智能云平台。</li><li><a href="https://github.com/ankane/blazer">Blazer</a> - 使商业智能变得简单。</li><li><a href="https://chartio.com">Chartio</a> - 商业智能平台，可以可视化和浏览我们的数据。</li><li><a href="https://www.datapine.com/">datapine</a> - 自助式商业智能工具。</li><li><a href="https://www.gooddata.com/">GoodData</a> - 商业智能和大数据分析软件。</li><li><a href="https://www.jaspersoft.com/">Jaspersoft</a> - 强大的商业智能套件。</li><li><a href="https://www.jedox.com/en/">Jedox Palo</a> - 可定制的商业智能平台.</li><li><a href="https://jethro.io/">Jethrodata</a> - 交互式大数据分析。</li><li><a href="https://intermix.io/">intermix.io</a> - Amazon Redshift 的性能监控</li><li><a href="https://github.com/metabase/metabase">Metabase</a> - 一个简单、开源的方式，通过给公司成员提问，从得到的数据中进行分析、学习。</li><li><a href="http://www.microsoft.com/en-us/server-cloud/solutions/business-intelligence/default.aspx">Microsoft</a> - 商业智能软件及平台。</li><li><a href="https://www.microstrategy.com/">Microstrategy</a> - 用于商业智能、移动智能和网络应用程序的软件平台。</li><li><a href="https://numeracy.co/">Numeracy</a> - 快速，干净的 SQL 客户端和商业智能。</li><li><a href="http://www.pentaho.com/">Pentaho</a> - 商业智能平台。</li><li><a href="http://www.qlik.com/us/">Qlik</a> - 商业智能及分析平台。</li><li><a href="https://redash.io/">Redash</a> - 开源商业智能平台，支持多个数据源和计划查询。</li><li><a href="https://www.meteorite.bi/">Saiku Analytics</a> - 开源分析平台。</li><li><a href="https://www.knowage-suite.com/">Knowage</a> - 开源商业智能平台。 前 <a href="http://www.spagobi.org/">SpagoBi</a>。</li><li><a href="http://sparklinedata.com/">SparklineData SNAP</a> - 基于 Apache Spark 的商业智能平台。</li><li><a href="https://www.tableau.com/">Tableau</a> - 商业智能平台。</li><li><a href="https://www.zoomdata.com/">Zoomdata</a> - 大数据分析平台。</li></ul><h2 id="数据可视化"><a href="#数据可视化" class="headerlink" title="数据可视化"></a>数据可视化</h2><ul><li><a href="https://github.com/airbnb/airpal">Airpal</a> - PrestoDB 的 Web UI。</li><li><a href="http://www.anychart.com">AnyChart</a> - 一套灵活的 JavaScript（HTML5）库，可满足您的所有数据可视化需求。</li><li><a href="https://github.com/samizdatco/arbor">Arbor</a> - 一个使用 web workers 和 jQuery 创建的图可视化库。</li><li><a href="https://github.com/LucidWorks/banana">Banana</a> - 可视化存储在 Solr 中的日志和带时间戳的数据，是 Kibana 的一部分。</li><li><a href="https://github.com/ufukomer/bloomery">Bloomery</a> - Impala 的 Web UI。</li><li><a href="http://bokeh.pydata.org/en/latest/">Bokeh</a> - 一个 Python 交互式可视化库，支持现代化 Web 浏览器，提供非常完美的展示功能。</li><li><a href="http://c3js.org/">C3</a> - 基于 D3 的可重用图表库。</li><li><a href="https://github.com/CartoDB/cartodb">CartoDB</a> - 开源的云上地理空间数据库，允许存储和可视化 web 上的数据。使用 CartoDB 可以快速创建基于地图的可视化效果。</li><li><a href="http://chartd.co/">chartd</a> - 响应式、视网膜兼容图表，仅需要一个 img 标签。</li><li><a href="http://www.chartjs.org/">Chart.js</a> - 一套开源、简单、干净并且有吸引力的基于 HTML5 技术的 JavaScript 图表工具。</li><li><a href="https://github.com/gionkunz/chartist-js">Chartist.js</a> - 非常简单而且实用的 JavaScript 前端图表生成器。</li><li><a href="http://square.github.io/crossfilter/">Crossfilter</a> -  一个 JavaScript 库，用于在 JavaScript 中制作交互式的仪表板，可以与 dc.js 、d3.js 一起工作。</li><li><a href="https://github.com/square/cubism">Cubism</a> - 用于时间序列可视化的 JavaScript 库。</li><li><a href="http://cytoscape.github.io/">Cytoscape</a> - 一个专注于网络可视化和分析的开源软件。</li><li><a href="http://dc-js.github.io/dc.js/">DC.js</a> - 一个用于网页作图、生成互动图形的 JavaScript 函数库。</li><li><a href="https://d3js.org/">D3</a> - 目前最流行的数据可视化库之一，小型，灵活，高效的数据可视化库，用来创建和操作基于数据的交互式文档。</li><li><a href="https://github.com/CSNW/d3.compose">D3.compose</a> - 由可重复使用的图表和组件组成复杂的、数据驱动的可视化文件。</li><li><a href="http://d3plus.org">D3Plus</a> - d3.js 的一组相当强大的可重用图表和样式。</li><li><a href="https://devexpress.github.io/devextreme-reactive/react/chart/">DevExtreme React Chart</a> - 基于高性能插件的 React 图表，用于 Bootstrap 和 Material Design。</li><li><a href="https://github.com/ecomfe/echarts">ECharts</a> - 一款由百度前端技术部开发的，基于 Javascript 的数据可视化图表库，提供直观，生动，可交互，可个性化定制的数据可视化图表。</li><li><a href="https://github.com/HumbleSoftware/envisionjs">Envisionjs</a> - 一个基于 HTML5 技术的数据可视化库。</li><li><a href="https://metrictools.org/">FnordMetric</a> - 一个开源的 Web 应用，可用于创建实时仪表板，方便可视化任何数据。</li><li><a href="https://frappe.io/charts">Frappe Charts</a> - 一个受 Github 启发的轻量级 SVG 图表库，它不依赖任何类库和框架。</li><li><a href="https://github.com/Freeboard/freeboard">Freeboard</a> - 让用户创建他们自己的用来监控物联网部署的仪表盘，该代码在 GitHub 上免费提供，你可以通过这些仪表板展示跟踪空气质量、住宅电器、酿酒情况和实时环境条件变化。</li><li><a href="https://github.com/gephi/gephi">Gephi</a> - 一款开源免费跨平台基于 JVM 的网络分析领域的数据可视化处理软件。</li><li><a href="https://developers.google.com/chart/">Google Charts</a> - 一种交互式 Web 服务，可根据用户提供的数据创建图形图表。</li><li><a href="https://grafana.com/">Grafana</a> - 一个跨平台的开源的度量分析和可视化工具，可以通过将采集的数据查询然后可视化的展示，并及时通知。</li><li><a href="http://graphiteapp.org/">Graphite</a> - 一款开源的监控绘图工具。</li><li><a href="https://www.highcharts.com/">Highcharts</a> - 兼容 IE6+、完美支持移动端、图表类型丰富、方便快捷的 HTML5 交互性图表库。</li><li><a href="http://ipython.org/">IPython</a> - 一种基于 Python 的交互式解释器。相较于原生的 Python Shell，IPython 提供了更为强大的编辑和交互功能。</li><li><a href="https://www.elastic.co/products/kibana">Kibana</a> - Elasticsearch 的开源数据可视化插件。</li><li><a href="http://lumify.io/">Lumify</a> - 开源大数据分析可视化平台。</li><li><a href="https://github.com/matplotlib/matplotlib">Matplotlib</a> - Python 编程语言及其数值数学扩展包 Numpy 的可视化操作界面。</li><li><a href="https://metricsgraphicsjs.org/">Metricsgraphic.js</a> - 一个建立在 D3 基础上，为可视化和时间序列化的数据而优化的库。</li><li><a href="http://nvd3.org/">NVD3</a> - d3.js 的图表组件。</li><li><a href="https://github.com/benpickles/peity">Peity</a> - 渐进式 SVG 条形图，折线图和饼图。</li><li><a href="https://plot.ly/">Plot.ly</a> - Plotly 为个人和协作提供在线图形，分析和统计工具，以及 Python，R，MATLAB，Perl，Julia，Arduino 和 REST 的科学图形库。</li><li><a href="https://github.com/plotly/plotly.js">Plotly.js</a> - 一个开源的交互式 JavaScript 图形库，建立在 d3.js 和 webgl 之上，并支持 20 多种类型的交互式图表。</li><li><a href="https://github.com/okfn/recline">Recline</a> - 简单而强大的库，可以使用纯 Javascript 和 HTML 构建数据应用程序。</li><li><a href="https://github.com/getredash/redash">Redash</a> - 查询和可视化数据的开源平台。</li><li><a href="http://recharts.org/">ReCharts</a> - 一个基于 React 组件的可组合图表库。</li><li><a href="http://shiny.rstudio.com/">Shiny</a> - R 的 Web 应用程序框架。</li><li><a href="https://github.com/jacomyal/sigma.js">Sigma.js</a> - 专门用于图形绘制的 JavaScript 库。</li><li><a href="https://github.com/apache/incubator-superset">Superset</a> - 由 Airbnb 开发并开源一个数据探索和可视化平台，设计用来提供直观的，可视化的，交互式的分析体验。</li><li><a href="https://github.com/vega/vega">Vega</a> - 一个可视化的语法。</li><li><a href="https://github.com/ZEPL/zeppelin">Zeppelin</a> - 一个基于 Web 的 notebook，提供交互数据分析和可视化。</li><li><a href="https://www.zingchart.com/">Zing Charts</a> - 一个功能强大的 JavaScript 图表。</li></ul><h2 id="物联网与传感器"><a href="#物联网与传感器" class="headerlink" title="物联网与传感器"></a>物联网与传感器</h2><ul><li><a href="http://edgent.apache.org/">Apache Edgent (Incubating)</a> - 一种编程模型和具有微内核风格的运行时，可嵌入到网关和小型的物联网设备中。</li><li><a href="https://azure.microsoft.com/en-us/services/iot-hub/">Azure IoT Hub</a> - 托管服务，支持 IoT 设备与 Azure 之间的双向通信。</li><li><a href="https://www.tempoiq.com/">TempoIQ</a> - 基于云计算的传感器分析。</li><li><a href="http://2lemetry.com/">2lemetry</a> - 物联网平台。</li><li><a href="https://www.pubnub.com/">Pubnub</a> - 数据流网络。</li><li><a href="https://www.thingworx.com/">ThingWorx</a> - 可用于查找数据来源，使数据与情境相关，合成数据，同时协调流程，以提供强大的 Web、移动和 AR 体验的平台。</li><li><a href="https://ifttt.com/">IFTTT</a> - 一个新生的网络服务平台，通过其他不同平台的条件来决定是否执行下一条命令。</li><li><a href="https://evrythng.com/">Evrything</a>- 使产品智能化。</li><li><a href="https://github.com/marty90/netlytics/">NetLytics</a> - 用于在 Spark 上处理网络数据的分析平台。</li></ul><h2 id="阅读材料"><a href="#阅读材料" class="headerlink" title="阅读材料"></a>阅读材料</h2><ul><li><a href="https://amplab.cs.berkeley.edu/benchmark/">Big Data Benchmark</a> - Redshift，Hive，Shark，Impala 和 Stiger/Tez 的基准。</li><li><a href="https://kkovacs.eu/cassandra-vs-mongodb-vs-couchdb-vs-redis">NoSQL Comparison</a> - Cassandra，MongoDB，CouchDB，Redis，Riak，HBase，Couchbase，Neo4j，Hypertable，Elasticsearch，Accumulo，VoltDB 和 Scalaris 的比较。</li><li><a href="https://www.datadoghq.com/blog/monitoring-kafka-performance-metrics?ref=awesome">Monitoring Kafka performance</a> - 监视 Apache Kafka 的指南，包括度量收集的本地方法。</li><li><a href="https://www.datadoghq.com/blog/monitor-hadoop-metrics?ref=awesome">Monitoring Hadoop performance</a> - 监视 Hadoop 的指南，概述了 Hadoop 体系结构以及度量收集的本机方法。</li><li><a href="https://www.datadoghq.com/blog/how-to-monitor-cassandra-performance-metrics/?ref=awesome">Monitoring Cassandra performance</a> - 监控 Cassandra 的指南，包括度量收集的本地方法。</li></ul><h2 id="论文"><a href="#论文" class="headerlink" title="论文"></a>论文</h2><h3 id="2015-2016"><a href="#2015-2016" class="headerlink" title="2015 - 2016"></a>2015 - 2016</h3><ul><li><a href="http://www.vldb.org/pvldb/vol8/p1804-ching.pdf">2015</a> - <strong>Facebook</strong> - One Trillion Edges: Graph Processing at Facebook-Scale.</li></ul><h3 id="2013-2014"><a href="#2013-2014" class="headerlink" title="2013 - 2014"></a>2013 - 2014</h3><ul><li><a href="http://infolab.stanford.edu/~ullman/mmds/book.pdf">2014</a> - <strong>Stanford</strong> - Mining of Massive Datasets.</li><li><a href="https://amplab.cs.berkeley.edu/wp-content/uploads/2013/03/eurosys13-paper83.pdf">2013</a> - <strong>AMPLab</strong> - Presto: Distributed Machine Learning and Graph Processing with Sparse Matrices.</li><li><a href="https://amplab.cs.berkeley.edu/wp-content/uploads/2013/01/dmx1.pdf">2013</a> - <strong>AMPLab</strong> - MLbase: A Distributed Machine-learning System.</li><li><a href="https://amplab.cs.berkeley.edu/wp-content/uploads/2013/02/shark_sigmod2013.pdf">2013</a> - <strong>AMPLab</strong> - Shark: SQL and Rich Analytics at Scale.</li><li><a href="https://amplab.cs.berkeley.edu/wp-content/uploads/2013/05/grades-graphx_with_fonts.pdf">2013</a> - <strong>AMPLab</strong> - GraphX: A Resilient Distributed Graph System on Spark.</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/40671.pdf">2013</a> - <strong>Google</strong> - HyperLogLog in Practice: Algorithmic Engineering of a State of The Art Cardinality Estimation Algorithm.</li><li><a href="http://research.microsoft.com/pubs/200169/now-vldb.pdf">2013</a> - <strong>Microsoft</strong> - Scalable Progressive Analytics on Big Data in the Cloud.</li><li><a href="http://static.druid.io/docs/druid.pdf">2013</a> - <strong>Metamarkets</strong> - Druid: A Real-time Analytical Data Store.</li><li><a href="http://db.disi.unitn.eu/pages/VLDBProgram/pdf/industry/p764-rae.pdf">2013</a> - <strong>Google</strong> - Online, Asynchronous Schema Change in F1.</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en/us/pubs/archive/41344.pdf">2013</a> - <strong>Google</strong> - F1: A Distributed SQL Database That Scales.</li><li><a href="http://db.disi.unitn.eu/pages/VLDBProgram/pdf/industry/p734-akidau.pdf">2013</a> - <strong>Google</strong> - MillWheel: Fault-Tolerant Stream Processing at Internet Scale.</li><li><a href="http://db.disi.unitn.eu/pages/VLDBProgram/pdf/industry/p767-wiener.pdf">2013</a> - <strong>Facebook</strong> - Scuba: Diving into Data at Facebook.</li><li><a href="http://db.disi.unitn.eu/pages/VLDBProgram/pdf/industry/p871-curtiss.pdf">2013</a> - <strong>Facebook</strong> - Unicorn: A System for Searching the Social Graph.</li><li><a href="https://www.usenix.org/system/files/conference/nsdi13/nsdi13-final170_update.pdf">2013</a> - <strong>Facebook</strong> - Scaling Memcache at Facebook.</li></ul><h3 id="2011-2012"><a href="#2011-2012" class="headerlink" title="2011 - 2012"></a>2011 - 2012</h3><ul><li><a href="http://vldb.org/pvldb/vol5/p1771_georgelee_vldb2012.pdf">2012</a> - <strong>Twitter</strong> - The Unified Logging Infrastructurefor Data Analytics at Twitter.</li><li><a href="https://amplab.cs.berkeley.edu/wp-content/uploads/2013/04/blinkdb_vldb12_demo.pdf">2012</a> - <strong>AMPLab</strong> - Blink and It’s Done: Interactive Queries on Very Large Data.</li><li><a href="https://www.usenix.org/system/files/login/articles/zaharia.pdf">2012</a> - <strong>AMPLab</strong> - Fast and Interactive Analytics over Hadoop Data with Spark.</li><li><a href="https://amplab.cs.berkeley.edu/wp-content/uploads/2012/03/mod482-xin1.pdf">2012</a> - <strong>AMPLab</strong> - Shark: Fast Data Analysis Using Coarse-grained Distributed Memory.</li><li><a href="https://www.usenix.org/legacy/event/nsdi11/tech/full_papers/Bolosky.pdf">2012</a> - <strong>Microsoft</strong> - Paxos Replicated State Machines as the Basis of a High-Performance Data Store.</li><li><a href="http://research.microsoft.com/pubs/178045/ppaoxs-paper29.pdf">2012</a> - <strong>Microsoft</strong> - Paxos Made Parallel.</li><li><a href="https://arxiv.org/pdf/1203.5485.pdf">2012</a> - <strong>AMPLab</strong> - BlinkDB: Queries with Bounded Errors and Bounded Response Times on Very Large Data.</li><li><a href="http://vldb.org/pvldb/vol5/p1436_alexanderhall_vldb2012.pdf">2012</a> - <strong>Google</strong> - Processing a trillion cells per mouse click.</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf">2012</a> - <strong>Google</strong> - Spanner: Google’s Globally-Distributed Database.</li><li><a href="https://amplab.cs.berkeley.edu/wp-content/uploads/2011/06/euro118-ananthanarayanan.pdf">2011</a> - <strong>AMPLab</strong> - Scarlett: Coping with Skewed Popularity Content in MapReduce Clusters.</li><li><a href="https://amplab.cs.berkeley.edu/wp-content/uploads/2011/06/Mesos-A-Platform-for-Fine-Grained-Resource-Sharing-in-the-Data-Center.pdf">2011</a> - <strong>AMPLab</strong> - Mesos: A Platform for Fine-Grained Resource Sharing in the Data Center.</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36971.pdf">2011</a> - <strong>Google</strong> - Megastore: Providing Scalable, Highly Available Storage for Interactive Services.</li></ul><h3 id="2001-2010"><a href="#2001-2010" class="headerlink" title="2001 - 2010"></a>2001 - 2010</h3><ul><li><a href="https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Beaver.pdf">2010</a> - <strong>Facebook</strong> - Finding a needle in Haystack: Facebook’s photo storage.</li><li><a href="https://amplab.cs.berkeley.edu/wp-content/uploads/2011/06/Spark-Cluster-Computing-with-Working-Sets.pdf">2010</a> - <strong>AMPLab</strong> - Spark: Cluster Computing with Working Sets.</li><li><a href="http://kowshik.github.io/JPregel/pregel_paper.pdf">2010</a> - <strong>Google</strong> - Pregel: A System for Large-Scale Graph Processing.</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36726.pdf">2010</a> - <strong>Google</strong> - Large-scale Incremental Processing Using Distributed Transactions and Notiﬁcations base of Percolator and Caffeine.</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36632.pdf">2010</a> - <strong>Google</strong> - Dremel: Interactive Analysis of Web-Scale Datasets.</li><li><a href="http://leoneu.github.io/">2010</a> - <strong>Yahoo</strong> - S4: Distributed Stream Computing Platform.</li><li><a href="http://www.cs.umd.edu/~abadi/papers/hadoopdb.pdf">2009</a> - HadoopDB: An Architectural Hybrid of MapReduce and DBMS Technologies for Analytical Workloads.    </li><li><a href="https://cwiki.apache.org/confluence/download/attachments/120729877/chukwa_cca08.pdf?version=1&amp;modificationDate=1562667399000&amp;api=v2">2008</a> - <strong>AMPLab</strong> - Chukwa: A large-scale monitoring system.</li><li><a href="http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf">2007</a> - <strong>Amazon</strong> - Dynamo: Amazon’s Highly Available Key-value Store.</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en//archive/chubby-osdi06.pdf">2006</a> - <strong>Google</strong> - The Chubby lock service for loosely-coupled distributed systems.</li><li><a href="http://static.googleusercontent.com/external_content/untrusted_dlcp/research.google.com/en//archive/bigtable-osdi06.pdf">2006</a> - <strong>Google</strong> - Bigtable: A Distributed Storage System for Structured Data.</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en//archive/mapreduce-osdi04.pdf">2004</a> - <strong>Google</strong> - MapReduce: Simplied Data Processing on Large Clusters.</li><li><a href="http://static.googleusercontent.com/media/research.google.com/en//archive/gfs-sosp2003.pdf">2003</a> - <strong>Google</strong> - The Google File System.</li></ul><h2 id="视频"><a href="#视频" class="headerlink" title="视频"></a>视频</h2><ul><li><a href="https://www.manning.com/livevideo/spark-in-motion">Spark in Motion</a> - Spark in Motion 教你如何使用 Spark 进行批处理和流数据分析。</li><li><a href="https://www.manning.com/livevideo/machine-learning-data-science-and-deep-learning-with-python">Machine Learning, Data Science and Deep Learning with Python </a> - LiveVideo 教程涵盖了机器学习，Tensorflow，人工智能和神经网络。</li></ul><h2 id="书籍"><a href="#书籍" class="headerlink" title="书籍"></a>书籍</h2><h3 id="数据流"><a href="#数据流" class="headerlink" title="数据流"></a>数据流</h3><ul><li><a href="https://www.manning.com/books/data-science-at-scale-with-python-and-dask">Data Science at Scale with Python and Dask</a> - 使用 Python 和 Dask 进行大规模数据科学教学，将教您如何构建可处理大量数据的分布式数据项目。</li><li><a href="https://www.manning.com/books/streaming-data">Streaming Data</a> - 介绍了流数据和实时数据系统的概念和要求。</li><li><a href="https://www.manning.com/books/storm-applied">Storm Applied</a> - 将 Apache Storm 用于与处理和分析实时数据流相关的实际任务的实用指南。</li><li><a href="http://www.cambridge.org/us/academic/subjects/engineering/communications-and-signal-processing/fundamentals-stream-processing-application-design-systems-and-analytics">Fundamentals of Stream Processing: Application Design, Systems, and Analytics</a> - 该指南结合了流处理中的基本构建块和新兴研究，非常适合应用程序设计人员，系统构建人员，分析开发人员以及该领域的学生和研究人员。</li><li><a href="http://www.springer.com/us/book/9780387710020">Stream Data Processing: A Quality of Service Perspective</a> - 提出了适用于流和复杂事件处理的新范例。</li><li><a href="https://www.manning.com/books/event-streams-in-action">Unified Log Processing</a> - 统一日志处理是在企业中实施事件流（Kafka 或 Kinesis）的统一日志的实用指南</li><li><a href="https://www.manning.com/books/kafka-streams-in-action">Kafka Streams in Action</a> - 在 Kafka 平台的数据上实现流处理所需的一切知识，可以集中精力从数据中获取更多信息，而无需花费时间或精力。</li><li><a href="https://www.manning.com/books/big-data">Big Data</a> - 教您使用一种架构来构建大数据系统，该架构利用了群集硬件以及专门用于捕获和分析 Web 规模数据的新工具。</li><li><a href="https://www.manning.com/books/spark-in-action">Spark in Action</a> &amp; <a href="https://www.manning.com/books/spark-in-action-second-edition">Spark in Action 2nd Ed.</a> - 教您使用 Spark 有效处理批处理和流式数据所需的理论和技能。 完全更新为 Spark 2.0。</li><li><a href="https://www.manning.com/books/kafka-in-action">Kafka in Action</a> - 快速介绍与 Kafka 合作的各个方面的信息，您需要真正利用它的好处。</li><li><a href="https://www.manning.com/books/fusion-in-action">Fusion in Action</a> - 教您建立功能齐全的数据分析管道，包括文档和数据搜索以及分布式数据集群。</li><li><a href="https://www.manning.com/books/reactive-data-handling">Reactive Data Handling</a> - Reactive Data Handling 是由 Manuel Bernhardt 选择的五个精选章节的集合，向您介绍构建能够处理大数据负载实时处理的反应式应用程序。</li></ul><h3 id="分布式系统"><a href="#分布式系统" class="headerlink" title="分布式系统"></a>分布式系统</h3><ul><li><a href="http://book.mixu.net/distsys/">Distributed Systems for fun and profit</a> - 分布式系统理论。 包括有关时间和顺序，复制和不可能结果的部分。</li></ul><h3 id="图论"><a href="#图论" class="headerlink" title="图论"></a>图论</h3><ul><li><a href="https://www.manning.com/books/graph-powered-machine-learning">Graph-Powered Machine Learning</a> - Alessandro Negro 结合图论和模型来改善机器学习项目。</li></ul><h3 id="数据可视化-1"><a href="#数据可视化-1" class="headerlink" title="数据可视化"></a>数据可视化</h3><ul><li><a href="https://www.youtube.com/watch?v=5Zg-C8AAIGg">The beauty of data visualization</a></li><li><a href="https://www.youtube.com/watch?v=R-oiKt7bUU8">Designing Data Visualizations with Noah Iliinsky</a></li><li><a href="https://www.youtube.com/watch?v=jbkSRLYSojo">Hans Rosling&#39;s 200 Countries, 200 Years, 4 Minutes</a></li><li><a href="https://www.youtube.com/watch?v=qTEchen97rQ">Ice Bucket Challenge Data Visualization</a></li></ul><h2 id="其他清单"><a href="#其他清单" class="headerlink" title="其他清单"></a>其他清单</h2><ul><li>Other awesome lists <a href="https://github.com/bayandin/awesome-awesomeness">awesome-awesomeness</a>.</li><li>Even more lists <a href="https://github.com/sindresorhus/awesome">awesome</a>.</li><li>Another list? <a href="https://github.com/jnv/lists">list</a>.</li><li>WTF! <a href="https://github.com/t3chnoboy/awesome-awesome-awesome">awesome-awesome-awesome</a>.</li><li>Analytics <a href="https://github.com/onurakpolat/awesome-analytics">awesome-analytics</a>.</li><li>Public Datasets <a href="https://github.com/awesomedata/awesome-public-datasets">awesome-public-datasets</a>.</li><li>Graph Classification <a href="https://github.com/benedekrozemberczki/awesome-graph-classification">awesome-graph-classification</a>.</li><li>Network Embedding <a href="https://github.com/chihming/awesome-network-embedding">awesome-network-embedding</a>.</li><li>Community Detection <a href="https://github.com/benedekrozemberczki/awesome-community-detection">awesome-community-detection</a>.</li><li>Decision Tree Papers <a href="https://github.com/benedekrozemberczki/awesome-decision-tree-papers">awesome-decision-tree-papers</a>.</li><li>Fraud Detection Papers <a href="https://github.com/benedekrozemberczki/awesome-fraud-detection-papers">awesome-fraud-detection-papers</a>.</li><li>Gradient Boosting Papers <a href="https://github.com/benedekrozemberczki/awesome-gradient-boosting-papers">awesome-gradient-boosting-papers</a>.</li><li>Monte Carlo Tree Search Papers <a href="https://github.com/benedekrozemberczki/awesome-monte-carlo-tree-search-papers">awesome-monte-carlo-tree-search-papers</a>.</li><li>Kafka <a href="https://github.com/monksy/awesome-kafka">awesome-kafka</a>.</li></ul>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/">大数据</category>
      
      <category domain="https://blog.eurkon.com/tags/%E6%96%87%E6%A1%A3/">文档</category>
      
      
      <comments>https://blog.eurkon.com/post/fc3d946f.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>在 Hexo 中插入 ECharts 动态图表</title>
      <link>https://blog.eurkon.com/post/6f565d8c.html</link>
      <guid>https://blog.eurkon.com/post/6f565d8c.html</guid>
      <pubDate>Tue, 05 Jan 2021 03:51:01 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;ECharts-简介&quot;&gt;&lt;a href=&quot;#ECharts-简介&quot; class=&quot;headerlink&quot; title=&quot;ECharts 简介&quot;&gt;&lt;/a&gt;ECharts 简介&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://echarts.apache.org/&quot;&gt;</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="ECharts-简介"><a href="#ECharts-简介" class="headerlink" title="ECharts 简介"></a>ECharts 简介</h2><p><a href="https://echarts.apache.org/">ECharts</a> 是一个使用 JavaScript 实现的开源可视化库，可以流畅的运行在 PC 和移动设备上，兼容当前绝大部分浏览器（Chrome，Firefox，Safari，IE8/9/10/11 等），底层依赖矢量图形库 ZRender，提供直观，交互丰富，可高度个性化定制的数据可视化图表。它提供了常规的折线图、柱状图、散点图、饼图、K 线图，用于统计的盒形图，用于地理数据可视化的地图、热力图、线图，用于关系数据可视化的关系图、treemap、旭日图，多维数据可视化的平行坐标，还有用于 BI 的漏斗图，仪表盘，并且支持图与图之间的混搭。</p><h2 id="Hexo-中的-ECharts"><a href="#Hexo-中的-ECharts" class="headerlink" title="Hexo 中的 ECharts"></a>Hexo 中的 ECharts</h2><p>ECharts 的 Hexo 插件目前有三个版本，分别为：</p><ul><li><a href="https://github.com/zhoulvjun/hexo-tag-echarts">hexo-tag-echarts</a><ul><li>需要手动引入 ECharts.js</li></ul></li><li><a href="https://github.com/kchen0x/hexo-tag-echarts3">hexo-tag-echarts3</a><ul><li>自动引入 <a href="https://cdn.bootcss.com/echarts/3.8.0/echarts.min.js">https://cdn.bootcss.com/echarts/3.8.0/echarts.min.js</a></li></ul></li><li><a href="https://github.com/gyx138/hexo-tag-echarts4">hexo-tag-echarts4</a><ul><li>自动引入 <a href="https://cdn.bootcss.com/echarts/4.3.0/echarts.min.js">https://cdn.bootcss.com/echarts/4.3.0/echarts.min.js</a></li></ul></li></ul><p>本文使用的是 Hexo 的 ECharts 插件 <a href="https://github.com/zhoulvjun/hexo-tag-echarts">hexo-tag-echarts</a>，只需要在 Hexo 文档中引入一次 ECharts.js 就可以渲染整个页面的图表了。插件的安装和使用非常的简单，只需要进入博客目录，然后打开命令行，用 <code>npm</code> 安装一下：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-tag-echarts --save</span><br></pre></td></tr></table></figure></p><p>之后在文章内使用 <code>echarts</code> 的 <code>tag</code> 就可以了</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27; %&#125;</span><br><span class="line">\\TODO echarts option goes here</span><br><span class="line">&#123;% endecharts %&#125;</span><br></pre></td></tr></table></figure></p><p>其中 <code>echarts</code> 是标签名，<code>endecharts</code> 是结束标签，不需要更改，<code>400</code> 是图表容器的高度，默认是按正常比例缩放的，<code>&#39;90%&#39;</code> 是图表容器的相对宽度，默认是 <code>100%</code>。在标签之间的部分，需要自己填充的图表数据和属性，更多自定义属性可以查看 <a href="https://echarts.apache.org/zh/option.html">ECharts 配置项文档</a>。</p><h2 id="图表示例"><a href="#图表示例" class="headerlink" title="图表示例"></a>图表示例</h2><p>现在你已经基本学会了在 Hexo 中插入 ECharts 图表了，下面再展示一些基本的图表，更多炫酷的图表可以自己去尝试一下。</p><h3 id="折线图"><a href="#折线图" class="headerlink" title="折线图"></a>折线图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27; %&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;</span></span><br><span class="line"><span class="code">        trigger: &#x27;axis&#x27;</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    xAxis: &#123;</span></span><br><span class="line"><span class="code">        type: &#x27;category&#x27;,</span></span><br><span class="line"><span class="code">        data: [&#x27;Mon&#x27;, &#x27;Tue&#x27;, &#x27;Wed&#x27;, &#x27;Thu&#x27;, &#x27;Fri&#x27;, &#x27;Sat&#x27;, &#x27;Sun&#x27;]</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    yAxis: &#123;</span></span><br><span class="line"><span class="code">        type: &#x27;value&#x27;</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    series: [&#123;</span></span><br><span class="line"><span class="code">        data: [820, 932, 901, 934, 1290, 1330, 1320],</span></span><br><span class="line"><span class="code">        type: &#x27;line&#x27;</span></span><br><span class="line"><span class="code">    &#125;]</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts6455" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts6455'));        // 指定图表的配置项和数据        var option = {    tooltip: {        trigger: 'axis'    },    xAxis: {        type: 'category',        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']    },    yAxis: {        type: 'value'    },    series: [{        data: [820, 932, 901, 934, 1290, 1330, 1320],        type: 'line'    }]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="柱状图、条形图"><a href="#柱状图、条形图" class="headerlink" title="柱状图、条形图"></a>柱状图、条形图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27; %&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;&#125;,</span></span><br><span class="line"><span class="code">    xAxis: &#123;</span></span><br><span class="line"><span class="code">        type: &#x27;category&#x27;,</span></span><br><span class="line"><span class="code">        data: [&#x27;Mon&#x27;, &#x27;Tue&#x27;, &#x27;Wed&#x27;, &#x27;Thu&#x27;, &#x27;Fri&#x27;, &#x27;Sat&#x27;, &#x27;Sun&#x27;]</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    yAxis: &#123;</span></span><br><span class="line"><span class="code">        type: &#x27;value&#x27;</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    series: [&#123;</span></span><br><span class="line"><span class="code">        data: [120, 200, 150, 80, 70, 110, 130],</span></span><br><span class="line"><span class="code">        type: &#x27;bar&#x27;</span></span><br><span class="line"><span class="code">    &#125;]</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts2176" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts2176'));        // 指定图表的配置项和数据        var option = {    tooltip: {},    xAxis: {        type: 'category',        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']    },    yAxis: {        type: 'value'    },    series: [{        data: [120, 200, 150, 80, 70, 110, 130],        type: 'bar'    }]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="环形图、饼状图"><a href="#环形图、饼状图" class="headerlink" title="环形图、饼状图"></a>环形图、饼状图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27; %&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;</span></span><br><span class="line"><span class="code">        trigger: &#x27;item&#x27;,</span></span><br><span class="line"><span class="code">        formatter: &#x27;&#123;a&#125; &lt;br/&gt;&#123;b&#125;: &#123;c&#125; (&#123;d&#125;%)&#x27;</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    legend: &#123;</span></span><br><span class="line"><span class="code">        orient: &#x27;vertical&#x27;,</span></span><br><span class="line"><span class="code">        left: 10,</span></span><br><span class="line"><span class="code">        data: [&#x27;直接访问&#x27;, &#x27;邮件营销&#x27;, &#x27;联盟广告&#x27;, &#x27;视频广告&#x27;, &#x27;搜索引擎&#x27;]</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    series: [</span></span><br><span class="line"><span class="code">        &#123;</span></span><br><span class="line"><span class="code">            name: &#x27;访问来源&#x27;,</span></span><br><span class="line"><span class="code">            type: &#x27;pie&#x27;,</span></span><br><span class="line"><span class="code">            radius: [&#x27;50%&#x27;, &#x27;70%&#x27;],</span></span><br><span class="line"><span class="code">            avoidLabelOverlap: false,</span></span><br><span class="line"><span class="code">            label: &#123;</span></span><br><span class="line"><span class="code">                show: false,</span></span><br><span class="line"><span class="code">                position: &#x27;center&#x27;</span></span><br><span class="line"><span class="code">            &#125;,</span></span><br><span class="line"><span class="code">            emphasis: &#123;</span></span><br><span class="line"><span class="code">                label: &#123;</span></span><br><span class="line"><span class="code">                    show: true,</span></span><br><span class="line"><span class="code">                    fontSize: &#x27;30&#x27;,</span></span><br><span class="line"><span class="code">                    fontWeight: &#x27;bold&#x27;</span></span><br><span class="line"><span class="code">                &#125;</span></span><br><span class="line"><span class="code">            &#125;,</span></span><br><span class="line"><span class="code">            labelLine: &#123;</span></span><br><span class="line"><span class="code">                show: false</span></span><br><span class="line"><span class="code">            &#125;,</span></span><br><span class="line"><span class="code">            data: [</span></span><br><span class="line"><span class="code">                &#123;value: 335, name: &#x27;直接访问&#x27;&#125;,</span></span><br><span class="line"><span class="code">                &#123;value: 310, name: &#x27;邮件营销&#x27;&#125;,</span></span><br><span class="line"><span class="code">                &#123;value: 234, name: &#x27;联盟广告&#x27;&#125;,</span></span><br><span class="line"><span class="code">                &#123;value: 135, name: &#x27;视频广告&#x27;&#125;,</span></span><br><span class="line"><span class="code">                &#123;value: 1548, name: &#x27;搜索引擎&#x27;&#125;</span></span><br><span class="line"><span class="code">            ]</span></span><br><span class="line"><span class="code">        &#125;</span></span><br><span class="line"><span class="code">    ]</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts544" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts544'));        // 指定图表的配置项和数据        var option = {    tooltip: {        trigger: 'item',        formatter: '{a} <br/>{b}: {c} ({d}%)'    },    legend: {        orient: 'vertical',        left: 10,        data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎']    },    series: [        {            name: '访问来源',            type: 'pie',            radius: ['50%', '70%'],            avoidLabelOverlap: false,            label: {                show: false,                position: 'center'            },            emphasis: {                label: {                    show: true,                    fontSize: '30',                    fontWeight: 'bold'                }            },            labelLine: {                show: false            },            data: [                {value: 335, name: '直接访问'},                {value: 310, name: '邮件营销'},                {value: 234, name: '联盟广告'},                {value: 135, name: '视频广告'},                {value: 1548, name: '搜索引擎'}            ]        }    ]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="散点图"><a href="#散点图" class="headerlink" title="散点图"></a>散点图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27; %&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;&#125;,</span></span><br><span class="line"><span class="code">    xAxis: &#123;&#125;,</span></span><br><span class="line"><span class="code">    yAxis: &#123;&#125;,</span></span><br><span class="line"><span class="code">    series: [&#123;</span></span><br><span class="line"><span class="code">        symbolSize: 20,</span></span><br><span class="line"><span class="code">        data: [</span></span><br><span class="line"><span class="code">            [10.0, 8.04],</span></span><br><span class="line"><span class="code">            [8.0, 6.95],</span></span><br><span class="line"><span class="code">            [13.0, 7.58],</span></span><br><span class="line"><span class="code">            [9.0, 8.81],</span></span><br><span class="line"><span class="code">            [11.0, 8.33],</span></span><br><span class="line"><span class="code">            [14.0, 9.96],</span></span><br><span class="line"><span class="code">            [6.0, 7.24],</span></span><br><span class="line"><span class="code">            [4.0, 4.26],</span></span><br><span class="line"><span class="code">            [12.0, 10.84],</span></span><br><span class="line"><span class="code">            [7.0, 4.82],</span></span><br><span class="line"><span class="code">            [5.0, 5.68]</span></span><br><span class="line"><span class="code">        ],</span></span><br><span class="line"><span class="code">        type: &#x27;scatter&#x27;</span></span><br><span class="line"><span class="code">    &#125;]</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：<div id="echarts316" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts316'));        // 指定图表的配置项和数据        var option = {    tooltip: {},    xAxis: {},    yAxis: {},    series: [{        symbolSize: 20,        data: [            [10.0, 8.04],            [8.0, 6.95],            [13.0, 7.58],            [9.0, 8.81],            [11.0, 8.33],            [14.0, 9.96],            [6.0, 7.24],            [4.0, 4.26],            [12.0, 10.84],            [7.0, 4.82],            [5.0, 5.68]        ],        type: 'scatter'    }]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="雷达图"><a href="#雷达图" class="headerlink" title="雷达图"></a>雷达图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27; %&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;&#125;,</span></span><br><span class="line"><span class="code">    legend: &#123;</span></span><br><span class="line"><span class="code">        data: [&#x27;预算分配（Allocated Budget）&#x27;, &#x27;实际开销（Actual Spending）&#x27;]</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    radar: &#123;</span></span><br><span class="line"><span class="code">        name: &#123;</span></span><br><span class="line"><span class="code">            textStyle: &#123;</span></span><br><span class="line"><span class="code">                color: &#x27;#fff&#x27;,</span></span><br><span class="line"><span class="code">                backgroundColor: &#x27;#999&#x27;,</span></span><br><span class="line"><span class="code">                borderRadius: 3,</span></span><br><span class="line"><span class="code">                padding: [3, 5]</span></span><br><span class="line"><span class="code">            &#125;</span></span><br><span class="line"><span class="code">        &#125;,</span></span><br><span class="line"><span class="code">        indicator: [</span></span><br><span class="line"><span class="code">            &#123; name: &#x27;销售（sales）&#x27;, max: 6500&#125;,</span></span><br><span class="line"><span class="code">            &#123; name: &#x27;管理（Administration）&#x27;, max: 16000&#125;,</span></span><br><span class="line"><span class="code">            &#123; name: &#x27;信息技术（Information Techology）&#x27;, max: 30000&#125;,</span></span><br><span class="line"><span class="code">            &#123; name: &#x27;客服（Customer Support）&#x27;, max: 38000&#125;,</span></span><br><span class="line"><span class="code">            &#123; name: &#x27;研发（Development）&#x27;, max: 52000&#125;,</span></span><br><span class="line"><span class="code">            &#123; name: &#x27;市场（Marketing）&#x27;, max: 25000&#125;</span></span><br><span class="line"><span class="code">        ]</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    series: [&#123;</span></span><br><span class="line"><span class="code">        name: &#x27;预算 vs 开销（Budget vs spending）&#x27;,</span></span><br><span class="line"><span class="code">        type: &#x27;radar&#x27;,</span></span><br><span class="line"><span class="code">        data: [</span></span><br><span class="line"><span class="code">            &#123;</span></span><br><span class="line"><span class="code">                value: [4300, 10000, 28000, 35000, 50000, 19000],</span></span><br><span class="line"><span class="code">                name: &#x27;预算分配（Allocated Budget）&#x27;</span></span><br><span class="line"><span class="code">            &#125;,</span></span><br><span class="line"><span class="code">            &#123;</span></span><br><span class="line"><span class="code">                value: [5000, 14000, 28000, 31000, 42000, 21000],</span></span><br><span class="line"><span class="code">                name: &#x27;实际开销（Actual Spending）&#x27;</span></span><br><span class="line"><span class="code">            &#125;</span></span><br><span class="line"><span class="code">        ]</span></span><br><span class="line"><span class="code">    &#125;]</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts396" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts396'));        // 指定图表的配置项和数据        var option = {    tooltip: {},    legend: {        data: ['预算分配（Allocated Budget）', '实际开销（Actual Spending）']    },    radar: {        name: {            textStyle: {                color: '#fff',                backgroundColor: '#999',                borderRadius: 3,                padding: [3, 5]            }        },        indicator: [            { name: '销售（sales）', max: 6500},            { name: '管理（Administration）', max: 16000},            { name: '信息技术（Information Techology）', max: 30000},            { name: '客服（Customer Support）', max: 38000},            { name: '研发（Development）', max: 52000},            { name: '市场（Marketing）', max: 25000}        ]    },    series: [{        name: '预算 vs 开销（Budget vs spending）',        type: 'radar',        data: [            {                value: [4300, 10000, 28000, 35000, 50000, 19000],                name: '预算分配（Allocated Budget）'            },            {                value: [5000, 14000, 28000, 31000, 42000, 21000],                name: '实际开销（Actual Spending）'            }        ]    }]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="关系图"><a href="#关系图" class="headerlink" title="关系图"></a>关系图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27; %&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;&#125;,</span></span><br><span class="line"><span class="code">    series: [</span></span><br><span class="line"><span class="code">        &#123;</span></span><br><span class="line"><span class="code">            type: &#x27;graph&#x27;,</span></span><br><span class="line"><span class="code">            layout: &#x27;none&#x27;,</span></span><br><span class="line"><span class="code">            symbolSize: 50,</span></span><br><span class="line"><span class="code">            label: &#123;</span></span><br><span class="line"><span class="code">                show: true</span></span><br><span class="line"><span class="code">            &#125;,</span></span><br><span class="line"><span class="code">            edgeSymbol: [&#x27;circle&#x27;, &#x27;arrow&#x27;],</span></span><br><span class="line"><span class="code">            edgeSymbolSize: [4, 10],</span></span><br><span class="line"><span class="code">            edgeLabel: &#123;</span></span><br><span class="line"><span class="code">                fontSize: 20</span></span><br><span class="line"><span class="code">            &#125;,</span></span><br><span class="line"><span class="code">            data: [&#123;</span></span><br><span class="line"><span class="code">                name: &#x27;节点1&#x27;,</span></span><br><span class="line"><span class="code">                x: 300,</span></span><br><span class="line"><span class="code">                y: 300</span></span><br><span class="line"><span class="code">            &#125;, &#123;</span></span><br><span class="line"><span class="code">                name: &#x27;节点2&#x27;,</span></span><br><span class="line"><span class="code">                x: 800,</span></span><br><span class="line"><span class="code">                y: 300</span></span><br><span class="line"><span class="code">            &#125;, &#123;</span></span><br><span class="line"><span class="code">                name: &#x27;节点3&#x27;,</span></span><br><span class="line"><span class="code">                x: 550,</span></span><br><span class="line"><span class="code">                y: 100</span></span><br><span class="line"><span class="code">            &#125;, &#123;</span></span><br><span class="line"><span class="code">                name: &#x27;节点4&#x27;,</span></span><br><span class="line"><span class="code">                x: 550,</span></span><br><span class="line"><span class="code">                y: 500</span></span><br><span class="line"><span class="code">            &#125;],</span></span><br><span class="line"><span class="code">            links: [&#123;</span></span><br><span class="line"><span class="code">                source: 0,</span></span><br><span class="line"><span class="code">                target: 1,</span></span><br><span class="line"><span class="code">                symbolSize: [5, 20],</span></span><br><span class="line"><span class="code">                label: &#123;</span></span><br><span class="line"><span class="code">                    show: true</span></span><br><span class="line"><span class="code">                &#125;,</span></span><br><span class="line"><span class="code">                lineStyle: &#123;</span></span><br><span class="line"><span class="code">                    width: 5,</span></span><br><span class="line"><span class="code">                    curveness: 0.2</span></span><br><span class="line"><span class="code">                &#125;</span></span><br><span class="line"><span class="code">            &#125;, &#123;</span></span><br><span class="line"><span class="code">                source: &#x27;节点2&#x27;,</span></span><br><span class="line"><span class="code">                target: &#x27;节点1&#x27;,</span></span><br><span class="line"><span class="code">                label: &#123;</span></span><br><span class="line"><span class="code">                    show: true</span></span><br><span class="line"><span class="code">                &#125;,</span></span><br><span class="line"><span class="code">                lineStyle: &#123;</span></span><br><span class="line"><span class="code">                    curveness: 0.2</span></span><br><span class="line"><span class="code">                &#125;</span></span><br><span class="line"><span class="code">            &#125;, &#123;</span></span><br><span class="line"><span class="code">                source: &#x27;节点1&#x27;,</span></span><br><span class="line"><span class="code">                target: &#x27;节点3&#x27;</span></span><br><span class="line"><span class="code">            &#125;, &#123;</span></span><br><span class="line"><span class="code">                source: &#x27;节点2&#x27;,</span></span><br><span class="line"><span class="code">                target: &#x27;节点3&#x27;</span></span><br><span class="line"><span class="code">            &#125;, &#123;</span></span><br><span class="line"><span class="code">                source: &#x27;节点2&#x27;,</span></span><br><span class="line"><span class="code">                target: &#x27;节点4&#x27;</span></span><br><span class="line"><span class="code">            &#125;, &#123;</span></span><br><span class="line"><span class="code">                source: &#x27;节点1&#x27;,</span></span><br><span class="line"><span class="code">                target: &#x27;节点4&#x27;</span></span><br><span class="line"><span class="code">            &#125;],</span></span><br><span class="line"><span class="code">            lineStyle: &#123;</span></span><br><span class="line"><span class="code">                opacity: 0.9,</span></span><br><span class="line"><span class="code">                width: 2,</span></span><br><span class="line"><span class="code">                curveness: 0</span></span><br><span class="line"><span class="code">            &#125;</span></span><br><span class="line"><span class="code">        &#125;</span></span><br><span class="line"><span class="code">    ]</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts4080" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts4080'));        // 指定图表的配置项和数据        var option = {    tooltip: {},    series: [        {            type: 'graph',            layout: 'none',            symbolSize: 50,            label: {                show: true            },            edgeSymbol: ['circle', 'arrow'],            edgeSymbolSize: [4, 10],            edgeLabel: {                fontSize: 20            },            data: [{                name: '节点1',                x: 300,                y: 300            }, {                name: '节点2',                x: 800,                y: 300            }, {                name: '节点3',                x: 550,                y: 100            }, {                name: '节点4',                x: 550,                y: 500            }],            links: [{                source: 0,                target: 1,                symbolSize: [5, 20],                label: {                    show: true                },                lineStyle: {                    width: 5,                    curveness: 0.2                }            }, {                source: '节点2',                target: '节点1',                label: {                    show: true                },                lineStyle: {                    curveness: 0.2                }            }, {                source: '节点1',                target: '节点3'            }, {                source: '节点2',                target: '节点3'            }, {                source: '节点2',                target: '节点4'            }, {                source: '节点1',                target: '节点4'            }],            lineStyle: {                opacity: 0.9,                width: 2,                curveness: 0            }        }    ]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="矩形树图"><a href="#矩形树图" class="headerlink" title="矩形树图"></a>矩形树图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27; %&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;&#125;,</span></span><br><span class="line"><span class="code">    series: [&#123;</span></span><br><span class="line"><span class="code">        type: &#x27;treemap&#x27;,</span></span><br><span class="line"><span class="code">        data: [&#123;</span></span><br><span class="line"><span class="code">            name: &#x27;nodeA&#x27;,            // First tree</span></span><br><span class="line"><span class="code">            value: 10,</span></span><br><span class="line"><span class="code">            children: [&#123;</span></span><br><span class="line"><span class="code">                name: &#x27;nodeAa&#x27;,       // First leaf of first tree</span></span><br><span class="line"><span class="code">                value: 4</span></span><br><span class="line"><span class="code">            &#125;, &#123;</span></span><br><span class="line"><span class="code">                name: &#x27;nodeAb&#x27;,       // Second leaf of first tree</span></span><br><span class="line"><span class="code">                value: 6</span></span><br><span class="line"><span class="code">            &#125;]</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            name: &#x27;nodeB&#x27;,            // Second tree</span></span><br><span class="line"><span class="code">            value: 20,</span></span><br><span class="line"><span class="code">            children: [&#123;</span></span><br><span class="line"><span class="code">                name: &#x27;nodeBa&#x27;,       // Son of first tree</span></span><br><span class="line"><span class="code">                value: 20,</span></span><br><span class="line"><span class="code">                children: [&#123;</span></span><br><span class="line"><span class="code">                    name: &#x27;nodeBa1&#x27;,  // Granson of first tree</span></span><br><span class="line"><span class="code">                    value: 20</span></span><br><span class="line"><span class="code">                &#125;]</span></span><br><span class="line"><span class="code">            &#125;]</span></span><br><span class="line"><span class="code">        &#125;]</span></span><br><span class="line"><span class="code">    &#125;]</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts2180" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts2180'));        // 指定图表的配置项和数据        var option = {    tooltip: {},    series: [{        type: 'treemap',        data: [{            name: 'nodeA',            // First tree            value: 10,            children: [{                name: 'nodeAa',       // First leaf of first tree                value: 4            }, {                name: 'nodeAb',       // Second leaf of first tree                value: 6            }]        }, {            name: 'nodeB',            // Second tree            value: 20,            children: [{                name: 'nodeBa',       // Son of first tree                value: 20,                children: [{                    name: 'nodeBa1',  // Granson of first tree                    value: 20                }]            }]        }]    }]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="旭日图"><a href="#旭日图" class="headerlink" title="旭日图"></a>旭日图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27; %&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;&#125;,</span></span><br><span class="line"><span class="code">    series: &#123;</span></span><br><span class="line"><span class="code">        type: &#x27;sunburst&#x27;,</span></span><br><span class="line"><span class="code">        data: [&#123;</span></span><br><span class="line"><span class="code">            name: &#x27;Grandpa&#x27;,</span></span><br><span class="line"><span class="code">            children: [&#123;</span></span><br><span class="line"><span class="code">                name: &#x27;Uncle Leo&#x27;,</span></span><br><span class="line"><span class="code">                value: 15,</span></span><br><span class="line"><span class="code">                children: [&#123;</span></span><br><span class="line"><span class="code">                    name: &#x27;Cousin Jack&#x27;,</span></span><br><span class="line"><span class="code">                    value: 2</span></span><br><span class="line"><span class="code">                &#125;, &#123;</span></span><br><span class="line"><span class="code">                    name: &#x27;Cousin Mary&#x27;,</span></span><br><span class="line"><span class="code">                    value: 5,</span></span><br><span class="line"><span class="code">                    children: [&#123;</span></span><br><span class="line"><span class="code">                        name: &#x27;Jackson&#x27;,</span></span><br><span class="line"><span class="code">                        value: 2</span></span><br><span class="line"><span class="code">                    &#125;]</span></span><br><span class="line"><span class="code">                &#125;, &#123;</span></span><br><span class="line"><span class="code">                    name: &#x27;Cousin Ben&#x27;,</span></span><br><span class="line"><span class="code">                    value: 4</span></span><br><span class="line"><span class="code">                &#125;]</span></span><br><span class="line"><span class="code">            &#125;, &#123;</span></span><br><span class="line"><span class="code">                name: &#x27;Father&#x27;,</span></span><br><span class="line"><span class="code">                value: 10,</span></span><br><span class="line"><span class="code">                children: [&#123;</span></span><br><span class="line"><span class="code">                    name: &#x27;Me&#x27;,</span></span><br><span class="line"><span class="code">                    value: 5</span></span><br><span class="line"><span class="code">                &#125;, &#123;</span></span><br><span class="line"><span class="code">                    name: &#x27;Brother Peter&#x27;,</span></span><br><span class="line"><span class="code">                    value: 1</span></span><br><span class="line"><span class="code">                &#125;]</span></span><br><span class="line"><span class="code">            &#125;]</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            name: &#x27;Nancy&#x27;,</span></span><br><span class="line"><span class="code">            children: [&#123;</span></span><br><span class="line"><span class="code">                name: &#x27;Uncle Nike&#x27;,</span></span><br><span class="line"><span class="code">                children: [&#123;</span></span><br><span class="line"><span class="code">                    name: &#x27;Cousin Betty&#x27;,</span></span><br><span class="line"><span class="code">                    value: 1</span></span><br><span class="line"><span class="code">                &#125;, &#123;</span></span><br><span class="line"><span class="code">                    name: &#x27;Cousin Jenny&#x27;,</span></span><br><span class="line"><span class="code">                    value: 2</span></span><br><span class="line"><span class="code">                &#125;]</span></span><br><span class="line"><span class="code">            &#125;]</span></span><br><span class="line"><span class="code">        &#125;],</span></span><br><span class="line"><span class="code">        radius: [0, &#x27;90%&#x27;],</span></span><br><span class="line"><span class="code">        label: &#123;</span></span><br><span class="line"><span class="code">            rotate: &#x27;radial&#x27;</span></span><br><span class="line"><span class="code">        &#125;</span></span><br><span class="line"><span class="code">    &#125;</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts4089" style="width: 90%;height: 700px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts4089'));        // 指定图表的配置项和数据        var option = {    tooltip: {},    series: {        type: 'sunburst',        data: [{            name: 'Grandpa',            children: [{                name: 'Uncle Leo',                value: 15,                children: [{                    name: 'Cousin Jack',                    value: 2                }, {                    name: 'Cousin Mary',                    value: 5,                    children: [{                        name: 'Jackson',                        value: 2                    }]                }, {                    name: 'Cousin Ben',                    value: 4                }]            }, {                name: 'Father',                value: 10,                children: [{                    name: 'Me',                    value: 5                }, {                    name: 'Brother Peter',                    value: 1                }]            }]        }, {            name: 'Nancy',            children: [{                name: 'Uncle Nike',                children: [{                    name: 'Cousin Betty',                    value: 1                }, {                    name: 'Cousin Jenny',                    value: 2                }]            }]        }],        radius: [0, '90%'],        label: {            rotate: 'radial'        }    }};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="平行坐标系"><a href="#平行坐标系" class="headerlink" title="平行坐标系"></a>平行坐标系</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27; %&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    parallelAxis: [</span></span><br><span class="line"><span class="code">        &#123;dim: 0, name: &#x27;Price&#x27;&#125;,</span></span><br><span class="line"><span class="code">        &#123;dim: 1, name: &#x27;Net Weight&#x27;&#125;,</span></span><br><span class="line"><span class="code">        &#123;dim: 2, name: &#x27;Amount&#x27;&#125;,</span></span><br><span class="line"><span class="code">        &#123;</span></span><br><span class="line"><span class="code">            dim: 3,</span></span><br><span class="line"><span class="code">            name: &#x27;Score&#x27;,</span></span><br><span class="line"><span class="code">            type: &#x27;category&#x27;,</span></span><br><span class="line"><span class="code">            data: [&#x27;Excellent&#x27;, &#x27;Good&#x27;, &#x27;OK&#x27;, &#x27;Bad&#x27;]</span></span><br><span class="line"><span class="code">        &#125;</span></span><br><span class="line"><span class="code">    ],</span></span><br><span class="line"><span class="code">    series: &#123;</span></span><br><span class="line"><span class="code">        type: &#x27;parallel&#x27;,</span></span><br><span class="line"><span class="code">        lineStyle: &#123;</span></span><br><span class="line"><span class="code">            width: 4</span></span><br><span class="line"><span class="code">        &#125;,</span></span><br><span class="line"><span class="code">        data: [</span></span><br><span class="line"><span class="code">            [12.99, 100, 82, &#x27;Good&#x27;],</span></span><br><span class="line"><span class="code">            [9.99, 80, 77, &#x27;OK&#x27;],</span></span><br><span class="line"><span class="code">            [20, 120, 60, &#x27;Excellent&#x27;]</span></span><br><span class="line"><span class="code">        ]</span></span><br><span class="line"><span class="code">    &#125;</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts6272" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts6272'));        // 指定图表的配置项和数据        var option = {    parallelAxis: [        {dim: 0, name: 'Price'},        {dim: 1, name: 'Net Weight'},        {dim: 2, name: 'Amount'},        {            dim: 3,            name: 'Score',            type: 'category',            data: ['Excellent', 'Good', 'OK', 'Bad']        }    ],    series: {        type: 'parallel',        lineStyle: {            width: 4        },        data: [            [12.99, 100, 82, 'Good'],            [9.99, 80, 77, 'OK'],            [20, 120, 60, 'Excellent']        ]    }};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="桑基图"><a href="#桑基图" class="headerlink" title="桑基图"></a>桑基图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27;&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;&#125;,</span></span><br><span class="line"><span class="code">    series: &#123;</span></span><br><span class="line"><span class="code">        type: &#x27;sankey&#x27;,</span></span><br><span class="line"><span class="code">        layout: &#x27;none&#x27;,</span></span><br><span class="line"><span class="code">        focusNodeAdjacency: &#x27;allEdges&#x27;,</span></span><br><span class="line"><span class="code">        data: [&#123;</span></span><br><span class="line"><span class="code">            name: &#x27;a&#x27;</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            name: &#x27;b&#x27;</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            name: &#x27;a1&#x27;</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            name: &#x27;a2&#x27;</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            name: &#x27;b1&#x27;</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            name: &#x27;c&#x27;</span></span><br><span class="line"><span class="code">        &#125;],</span></span><br><span class="line"><span class="code">        links: [&#123;</span></span><br><span class="line"><span class="code">            source: &#x27;a&#x27;,</span></span><br><span class="line"><span class="code">            target: &#x27;a1&#x27;,</span></span><br><span class="line"><span class="code">            value: 5</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            source: &#x27;a&#x27;,</span></span><br><span class="line"><span class="code">            target: &#x27;a2&#x27;,</span></span><br><span class="line"><span class="code">            value: 3</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            source: &#x27;b&#x27;,</span></span><br><span class="line"><span class="code">            target: &#x27;b1&#x27;,</span></span><br><span class="line"><span class="code">            value: 8</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            source: &#x27;a&#x27;,</span></span><br><span class="line"><span class="code">            target: &#x27;b1&#x27;,</span></span><br><span class="line"><span class="code">            value: 3</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            source: &#x27;b1&#x27;,</span></span><br><span class="line"><span class="code">            target: &#x27;a1&#x27;,</span></span><br><span class="line"><span class="code">            value: 1</span></span><br><span class="line"><span class="code">        &#125;, &#123;</span></span><br><span class="line"><span class="code">            source: &#x27;b1&#x27;,</span></span><br><span class="line"><span class="code">            target: &#x27;c&#x27;,</span></span><br><span class="line"><span class="code">            value: 2</span></span><br><span class="line"><span class="code">        &#125;]</span></span><br><span class="line"><span class="code">    &#125;</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts3730" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts3730'));        // 指定图表的配置项和数据        var option = {    tooltip: {},    series: {        type: 'sankey',        layout: 'none',        focusNodeAdjacency: 'allEdges',        data: [{            name: 'a'        }, {            name: 'b'        }, {            name: 'a1'        }, {            name: 'a2'        }, {            name: 'b1'        }, {            name: 'c'        }],        links: [{            source: 'a',            target: 'a1',            value: 5        }, {            source: 'a',            target: 'a2',            value: 3        }, {            source: 'b',            target: 'b1',            value: 8        }, {            source: 'a',            target: 'b1',            value: 3        }, {            source: 'b1',            target: 'a1',            value: 1        }, {            source: 'b1',            target: 'c',            value: 2        }]    }};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="漏斗图"><a href="#漏斗图" class="headerlink" title="漏斗图"></a>漏斗图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27;&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;</span></span><br><span class="line"><span class="code">        trigger: &#x27;item&#x27;,</span></span><br><span class="line"><span class="code">        formatter: &quot;&#123;a&#125; &lt;br/&gt;&#123;b&#125; : &#123;c&#125;%&quot;</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    legend: &#123;</span></span><br><span class="line"><span class="code">        data: [&#x27;展现&#x27;,&#x27;点击&#x27;,&#x27;访问&#x27;,&#x27;咨询&#x27;,&#x27;订单&#x27;]</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    series: [</span></span><br><span class="line"><span class="code">        &#123;</span></span><br><span class="line"><span class="code">            name:&#x27;漏斗图&#x27;,</span></span><br><span class="line"><span class="code">            type:&#x27;funnel&#x27;,</span></span><br><span class="line"><span class="code">            left: &#x27;10%&#x27;,</span></span><br><span class="line"><span class="code">            top: 60,</span></span><br><span class="line"><span class="code">            //x2: 80,</span></span><br><span class="line"><span class="code">            bottom: 60,</span></span><br><span class="line"><span class="code">            width: &#x27;80%&#x27;,</span></span><br><span class="line"><span class="code">            // height: &#123;totalHeight&#125; - y - y2,</span></span><br><span class="line"><span class="code">            min: 0,</span></span><br><span class="line"><span class="code">            max: 100,</span></span><br><span class="line"><span class="code">            minSize: &#x27;0%&#x27;,</span></span><br><span class="line"><span class="code">            maxSize: &#x27;100%&#x27;,</span></span><br><span class="line"><span class="code">            sort: &#x27;descending&#x27;,</span></span><br><span class="line"><span class="code">            gap: 2,</span></span><br><span class="line"><span class="code">            label: &#123;</span></span><br><span class="line"><span class="code">                show: true,</span></span><br><span class="line"><span class="code">                position: &#x27;inside&#x27;</span></span><br><span class="line"><span class="code">            &#125;,</span></span><br><span class="line"><span class="code">            labelLine: &#123;</span></span><br><span class="line"><span class="code">                length: 10,</span></span><br><span class="line"><span class="code">                lineStyle: &#123;</span></span><br><span class="line"><span class="code">                    width: 1,</span></span><br><span class="line"><span class="code">                    type: &#x27;solid&#x27;</span></span><br><span class="line"><span class="code">                &#125;</span></span><br><span class="line"><span class="code">            &#125;,</span></span><br><span class="line"><span class="code">            itemStyle: &#123;</span></span><br><span class="line"><span class="code">                borderColor: &#x27;#fff&#x27;,</span></span><br><span class="line"><span class="code">                borderWidth: 1</span></span><br><span class="line"><span class="code">            &#125;,</span></span><br><span class="line"><span class="code">            data: [</span></span><br><span class="line"><span class="code">                &#123;value: 60, name: &#x27;访问&#x27;&#125;,</span></span><br><span class="line"><span class="code">                &#123;value: 40, name: &#x27;咨询&#x27;&#125;,</span></span><br><span class="line"><span class="code">                &#123;value: 20, name: &#x27;订单&#x27;&#125;,</span></span><br><span class="line"><span class="code">                &#123;value: 80, name: &#x27;点击&#x27;&#125;,</span></span><br><span class="line"><span class="code">                &#123;value: 100, name: &#x27;展现&#x27;&#125;</span></span><br><span class="line"><span class="code">            ]</span></span><br><span class="line"><span class="code">        &#125;</span></span><br><span class="line"><span class="code">    ]</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts1294" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts1294'));        // 指定图表的配置项和数据        var option = {    tooltip: {        trigger: 'item',        formatter: "{a} <br/>{b} : {c}%"    },    legend: {        data: ['展现','点击','访问','咨询','订单']    },    series: [        {            name:'漏斗图',            type:'funnel',            left: '10%',            top: 60,            //x2: 80,            bottom: 60,            width: '80%',            // height: {totalHeight} - y - y2,            min: 0,            max: 100,            minSize: '0%',            maxSize: '100%',            sort: 'descending',            gap: 2,            label: {                show: true,                position: 'inside'            },            labelLine: {                length: 10,                lineStyle: {                    width: 1,                    type: 'solid'                }            },            itemStyle: {                borderColor: '#fff',                borderWidth: 1            },            data: [                {value: 60, name: '访问'},                {value: 40, name: '咨询'},                {value: 20, name: '订单'},                {value: 80, name: '点击'},                {value: 100, name: '展现'}            ]        }    ]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="仪表盘"><a href="#仪表盘" class="headerlink" title="仪表盘"></a>仪表盘</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&#123;% echarts 400 &#x27;90%&#x27;&#125;</span><br><span class="line">&#123;</span><br><span class="line"><span class="code">    tooltip: &#123;</span></span><br><span class="line"><span class="code">        formatter: &#x27;&#123;a&#125; &lt;br/&gt;&#123;b&#125; : &#123;c&#125;%&#x27;</span></span><br><span class="line"><span class="code">    &#125;,</span></span><br><span class="line"><span class="code">    series: [</span></span><br><span class="line"><span class="code">        &#123;</span></span><br><span class="line"><span class="code">            name: &#x27;业务指标&#x27;,</span></span><br><span class="line"><span class="code">            type: &#x27;gauge&#x27;,</span></span><br><span class="line"><span class="code">            detail: &#123;formatter: &#x27;&#123;value&#125;%&#x27;&#125;,</span></span><br><span class="line"><span class="code">            data: [&#123;value: 88.88, name: &#x27;完成率&#x27;&#125;]</span></span><br><span class="line"><span class="code">        &#125;</span></span><br><span class="line"><span class="code">    ]</span></span><br><span class="line"><span class="code">&#125;;</span></span><br><span class="line"><span class="code">&#123;% endecharts %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="echarts3887" style="width: 90%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts3887'));        // 指定图表的配置项和数据        var option = {    tooltip: {        formatter: '{a} <br/>{b} : {c}%'    },    series: [        {            name: '业务指标',            type: 'gauge',            detail: {formatter: '{value}%'},            data: [{value: 88.88, name: '完成率'}]        }    ]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>如果你想了解更多，可以查看 ECharts 的 <a href="https://echarts.apache.org/en/index.html">英文文档</a> 或者 <a href="https://echarts.apache.org/zh/index.html">中文文档</a>，更多的实例和属性设置都可以找到，也可以在 ECharts 官网配置好图表属性后再引入到自己的 Hexo 文档中。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/ECharts/">ECharts</category>
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      
      <comments>https://blog.eurkon.com/post/6f565d8c.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>在 Hexo 中插入 Chart 动态图表</title>
      <link>https://blog.eurkon.com/post/a6e75a2b.html</link>
      <guid>https://blog.eurkon.com/post/a6e75a2b.html</guid>
      <pubDate>Mon, 04 Jan 2021 01:38:32 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;文章来源：
作者: &lt;a href=&quot;https://github.com/Shen-Yu&quot;&gt;Shen-Yu&lt;/a&gt;
链接: &lt;a href=&quot;https://shen-yu.gitee.io/2020/chartjs/&quot;&gt;https://shen</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>文章来源：作者: <a href="https://github.com/Shen-Yu">Shen-Yu</a>链接: <a href="https://shen-yu.gitee.io/2020/chartjs/">https://shen-yu.gitee.io/2020/chartjs/</a>来源: <a href="https://shen-yu.gitee.io/">岛</a>著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。</p></blockquote><h2 id="Chartjs-简介"><a href="#Chartjs-简介" class="headerlink" title="Chartjs 简介"></a>Chartjs 简介</h2><p><a href="https://chartjs.bootcss.com/">Chartjs</a> 是一款简单优雅的数据可视化工具，对比其他图表库如 <a href="https://echarts.apache.org/">ECharts</a>、<a href="https://www.highcharts.com/">Highcharts</a>、<a href="https://c3js.org/">C3</a>、<a href="http://www.flotcharts.org/">Flot</a>、<a href="https://www.amcharts.com/javascript-charts/">amCharts</a> 等，它的画面效果、动态效果都更精致，它的 <a href="https://chartjs.bootcss.com/">文档首页</a> 就透出一股小清新，基于 HTML5 Canvas，它拥有更好的性能且响应式，基本满足了一般数据展示的需要，包括折线图，条形图，饼图，散点图，雷达图，极地图，甜甜圈图等。</p><h2 id="Hexo-中的-Chartjs"><a href="#Hexo-中的-Chartjs" class="headerlink" title="Hexo 中的 Chartjs"></a>Hexo 中的 Chartjs</h2><p>为了方便在 Hexo 中使用这么漂亮的图表库，我自己写了一个 Hexo 的 <a href="https://github.com/Shen-Yu/hexo-tag-chart">Chartjs</a> 插件。插件的安装和使用非常的简单，只需要进入博客目录，然后打开命令行，用 <code>npm</code> 安装一下：</p><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-tag-chart --save</span><br></pre></td></tr></table></figure></p><p>之后在文章内使用 <code>chart</code> 的 <code>tag</code> 就可以了</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;% chart 90% 300 %&#125;</span><br><span class="line">\\TODO option goes here</span><br><span class="line">&#123;% endchart %&#125;</span><br></pre></td></tr></table></figure></p><p>其中 <code>chart</code> 是标签名，<code>endchart</code> 是结束标签，不需要更改，<code>90%</code> 是图表容器的相对宽度，默认是 <code>100%</code>，<code>300</code> 是图表容器的高度，默认是按正常比例缩放的，你可以通过设置 <code>options</code> 里面的 <code>aspectRatio</code> 属性来调整宽高比例，另外还有许多属性可以自定义，你可以查看 <a href="https://chartjs.bootcss.com/docs/">官方文档</a>。在标签之间的部分，需要自己填充的图表数据和属性。</p><h2 id="图表示例"><a href="#图表示例" class="headerlink" title="图表示例"></a>图表示例</h2><p>现在你已经基本学会了在 Hexo 中插入 Chart 图表了，下面再展示一些基本的图表，更多炫酷的图表可以自己去尝试一下。</p><h3 id="柱状图、条形图"><a href="#柱状图、条形图" class="headerlink" title="柱状图、条形图"></a>柱状图、条形图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">&#123;% chart 90% 300 %&#125;</span><br><span class="line">&#123;</span><br><span class="line">  type: &#x27;bar&#x27;,</span><br><span class="line">  data: &#123;</span><br><span class="line"><span class="code">    labels: [&#x27;January&#x27;, &#x27;February&#x27;, &#x27;March&#x27;, &#x27;April&#x27;, &#x27;May&#x27;, &#x27;June&#x27;, &#x27;July&#x27;],</span></span><br><span class="line"><span class="code">    datasets: [&#123;</span></span><br><span class="line"><span class="code">      label: &#x27;First Dataset&#x27;,</span></span><br><span class="line"><span class="code">      data: [65, 59, 80, 81, 56, 55, 40],</span></span><br><span class="line"><span class="code">      fill: false,</span></span><br><span class="line"><span class="code">      backgroundColor: [&#x27;rgba(255, 99, 132, 0.5)&#x27;, &#x27;rgba(255, 159, 64, 0.5)&#x27;, &#x27;rgba(255, 205, 86, 0.5)&#x27;, &#x27;rgba(75, 192, 192, 0.5)&#x27;, &#x27;rgba(54, 162, 235, 0.5)&#x27;, &#x27;rgba(153, 102, 255, 0.5)&#x27;, &#x27;rgba(201, 203, 207, 0.5)&#x27;],</span></span><br><span class="line"><span class="code">      borderColor: [&#x27;rgb(255, 99, 132)&#x27;, &#x27;rgb(255, 159, 64)&#x27;, &#x27;rgb(255, 205, 86)&#x27;, &#x27;rgb(75, 192, 192)&#x27;, &#x27;rgb(54, 162, 235)&#x27;, &#x27;rgb(153, 102, 255)&#x27;, &#x27;rgb(201, 203, 207)&#x27;],</span></span><br><span class="line"><span class="code">      borderWidth: 1</span></span><br><span class="line"><span class="code">    &#125;]</span></span><br><span class="line"><span class="code">  &#125;,</span></span><br><span class="line"><span class="code">  options: &#123;</span></span><br><span class="line"><span class="code">    scales: &#123;</span></span><br><span class="line"><span class="code">      yAxes: [&#123;</span></span><br><span class="line"><span class="code">        ticks: &#123;</span></span><br><span class="line"><span class="code">          beginAtZero: true</span></span><br><span class="line"><span class="code">        &#125;</span></span><br><span class="line"><span class="code">      &#125;]</span></span><br><span class="line"><span class="code">    &#125;</span></span><br><span class="line"><span class="code">  &#125;</span></span><br><span class="line"><span class="code">&#125;</span></span><br><span class="line"><span class="code">&#123;% endchart %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div style="width: 90%;margin: 0 auto">    <canvas id="chart8498" style="height: 300px"></canvas></div><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script><script type="text/javascript">    var ctx = document.getElementById('chart8498').getContext('2d');    var options = {  type: 'bar',  data: {    labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],    datasets: [{      label: 'First Dataset',      data: [65, 59, 80, 81, 56, 55, 40],      fill: false,      backgroundColor: ['rgba(255, 99, 132, 0.5)', 'rgba(255, 159, 64, 0.5)', 'rgba(255, 205, 86, 0.5)', 'rgba(75, 192, 192, 0.5)', 'rgba(54, 162, 235, 0.5)', 'rgba(153, 102, 255, 0.5)', 'rgba(201, 203, 207, 0.5)'],      borderColor: ['rgb(255, 99, 132)', 'rgb(255, 159, 64)', 'rgb(255, 205, 86)', 'rgb(75, 192, 192)', 'rgb(54, 162, 235)', 'rgb(153, 102, 255)', 'rgb(201, 203, 207)'],      borderWidth: 1    }]  },  options: {    scales: {      yAxes: [{        ticks: {          beginAtZero: true        }      }]    }  }};    new Chart(ctx, options);</script></p><h3 id="折线图"><a href="#折线图" class="headerlink" title="折线图"></a>折线图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">&#123;% chart 90% 300 %&#125;</span><br><span class="line">&#123;</span><br><span class="line">  type: &#x27;line&#x27;,</span><br><span class="line">  data: &#123;</span><br><span class="line"><span class="code">    labels: [&#x27;January&#x27;, &#x27;February&#x27;, &#x27;March&#x27;, &#x27;April&#x27;, &#x27;May&#x27;, &#x27;June&#x27;, &#x27;July&#x27;],</span></span><br><span class="line"><span class="code">    datasets: [&#123;</span></span><br><span class="line"><span class="code">      label: &#x27;First dataset&#x27;,</span></span><br><span class="line"><span class="code">      backgroundColor: &#x27;rgb(255, 99, 132)&#x27;,</span></span><br><span class="line"><span class="code">      borderColor: &#x27;rgb(255, 99, 132)&#x27;,</span></span><br><span class="line"><span class="code">      data: [0, 10, 5, 2, 20, 30, 45]</span></span><br><span class="line"><span class="code">      &#125;]</span></span><br><span class="line"><span class="code">  &#125;,</span></span><br><span class="line"><span class="code">  options: &#123;</span></span><br><span class="line"><span class="code">    responsive: true,</span></span><br><span class="line"><span class="code">    title: &#123;</span></span><br><span class="line"><span class="code">    display: true,</span></span><br><span class="line"><span class="code">    text: &#x27;Chart.js Line Chart&#x27;</span></span><br><span class="line"><span class="code">    &#125;</span></span><br><span class="line"><span class="code">  &#125;</span></span><br><span class="line"><span class="code">&#125;</span></span><br><span class="line"><span class="code">&#123;% endchart %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div style="width: 90%;margin: 0 auto">    <canvas id="chart5011" style="height: 300px"></canvas></div><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script><script type="text/javascript">    var ctx = document.getElementById('chart5011').getContext('2d');    var options = {  type: 'line',  data: {    labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],    datasets: [{      label: 'First dataset',      backgroundColor: 'rgb(255, 99, 132)',      borderColor: 'rgb(255, 99, 132)',      data: [0, 10, 5, 2, 20, 30, 45]      }]  },  options: {    responsive: true,    title: {    display: true,    text: 'Chart.js Line Chart'    }  }};    new Chart(ctx, options);</script></p><h3 id="环形图、饼状图"><a href="#环形图、饼状图" class="headerlink" title="环形图、饼状图"></a>环形图、饼状图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&#123;% chart 90% 300 %&#125;</span><br><span class="line">&#123;</span><br><span class="line">  type: &#x27;pie&#x27;,</span><br><span class="line">  data: &#123;</span><br><span class="line"><span class="code">    labels: [&#x27;Red&#x27;, &#x27;Blue&#x27;, &#x27;Yellow&#x27;],</span></span><br><span class="line"><span class="code">    datasets: [&#123;</span></span><br><span class="line"><span class="code">      label: &#x27;First Dataset&#x27;,</span></span><br><span class="line"><span class="code">      data: [300, 50, 100],</span></span><br><span class="line"><span class="code">      backgroundColor: [&#x27;rgb(255, 99, 132)&#x27;, &#x27;rgb(54, 162, 235)&#x27;, &#x27;rgb(255, 205, 86)&#x27;]</span></span><br><span class="line"><span class="code">    &#125;]</span></span><br><span class="line"><span class="code">  &#125;</span></span><br><span class="line"><span class="code">&#125;</span></span><br><span class="line"><span class="code">&#123;% endchart %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div style="width: 90%;margin: 0 auto">    <canvas id="chart9745" style="height: 300px"></canvas></div><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script><script type="text/javascript">    var ctx = document.getElementById('chart9745').getContext('2d');    var options = {  type: 'pie',  data: {    labels: ['Red', 'Blue', 'Yellow'],    datasets: [{      label: 'First Dataset',      data: [300, 50, 100],      backgroundColor: ['rgb(255, 99, 132)', 'rgb(54, 162, 235)', 'rgb(255, 205, 86)']    }]  }};    new Chart(ctx, options);</script></p><h3 id="雷达图"><a href="#雷达图" class="headerlink" title="雷达图"></a>雷达图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">&#123;% chart 90% 300 %&#125;</span><br><span class="line">&#123;</span><br><span class="line">  type: &#x27;radar&#x27;,</span><br><span class="line">  data: &#123;</span><br><span class="line"><span class="code">    labels: [&#x27;Eating&#x27;, &#x27;Drinking&#x27;, &#x27;Sleeping&#x27;, &#x27;Designing&#x27;, &#x27;Coding&#x27;, &#x27;Cycling&#x27;, &#x27;Running&#x27;],</span></span><br><span class="line"><span class="code">    datasets: [&#123;</span></span><br><span class="line"><span class="code">      label: &#x27;First Dataset&#x27;,</span></span><br><span class="line"><span class="code">      data: [65, 59, 90, 81, 56, 55, 40],</span></span><br><span class="line"><span class="code">      fill: true,</span></span><br><span class="line"><span class="code">      backgroundColor: &#x27;rgba(255, 99, 132, 0.2)&#x27;,</span></span><br><span class="line"><span class="code">      borderColor: &#x27;rgb(255, 99, 132)&#x27;,</span></span><br><span class="line"><span class="code">      pointBackgroundColor: &#x27;rgb(255, 99, 132)&#x27;,</span></span><br><span class="line"><span class="code">      pointBorderColor: &#x27;#fff&#x27;,</span></span><br><span class="line"><span class="code">      pointHoverBackgroundColor: &#x27;#fff&#x27;,</span></span><br><span class="line"><span class="code">      pointHoverBorderColor: &#x27;rgb(255, 99, 132)&#x27;</span></span><br><span class="line"><span class="code">    &#125;, &#123;</span></span><br><span class="line"><span class="code">      label: &#x27;Second Dataset&#x27;,</span></span><br><span class="line"><span class="code">      data: [28, 48, 40, 19, 96, 27, 100],</span></span><br><span class="line"><span class="code">      fill: true,</span></span><br><span class="line"><span class="code">      backgroundColor: &#x27;rgba(54, 162, 235, 0.2)&#x27;,</span></span><br><span class="line"><span class="code">      borderColor: &#x27;rgb(54, 162, 235)&#x27;,</span></span><br><span class="line"><span class="code">      pointBackgroundColor: &#x27;rgb(54, 162, 235)&#x27;,</span></span><br><span class="line"><span class="code">      pointBorderColor: &#x27;#fff&#x27;,</span></span><br><span class="line"><span class="code">      pointHoverBackgroundColor: &#x27;#fff&#x27;,</span></span><br><span class="line"><span class="code">      pointHoverBorderColor: &#x27;rgb(54, 162, 235)&#x27;</span></span><br><span class="line"><span class="code">    &#125;]</span></span><br><span class="line"><span class="code">  &#125;,</span></span><br><span class="line"><span class="code">  options: &#123;</span></span><br><span class="line"><span class="code">    elements: &#123;</span></span><br><span class="line"><span class="code">      line: &#123;</span></span><br><span class="line"><span class="code">        tension: 0,</span></span><br><span class="line"><span class="code">        borderWidth: 3</span></span><br><span class="line"><span class="code">      &#125;</span></span><br><span class="line"><span class="code">    &#125;</span></span><br><span class="line"><span class="code">  &#125;</span></span><br><span class="line"><span class="code">&#125;</span></span><br><span class="line"><span class="code">&#123;% endchart %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div style="width: 90%;margin: 0 auto">    <canvas id="chart3260" style="height: 300px"></canvas></div><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script><script type="text/javascript">    var ctx = document.getElementById('chart3260').getContext('2d');    var options = {  type: 'radar',  data: {    labels: ['Eating', 'Drinking', 'Sleeping', 'Designing', 'Coding', 'Cycling', 'Running'],    datasets: [{      label: 'First Dataset',      data: [65, 59, 90, 81, 56, 55, 40],      fill: true,      backgroundColor: 'rgba(255, 99, 132, 0.2)',      borderColor: 'rgb(255, 99, 132)',      pointBackgroundColor: 'rgb(255, 99, 132)',      pointBorderColor: '#fff',      pointHoverBackgroundColor: '#fff',      pointHoverBorderColor: 'rgb(255, 99, 132)'    }, {      label: 'Second Dataset',      data: [28, 48, 40, 19, 96, 27, 100],      fill: true,      backgroundColor: 'rgba(54, 162, 235, 0.2)',      borderColor: 'rgb(54, 162, 235)',      pointBackgroundColor: 'rgb(54, 162, 235)',      pointBorderColor: '#fff',      pointHoverBackgroundColor: '#fff',      pointHoverBorderColor: 'rgb(54, 162, 235)'    }]  },  options: {    elements: {      line: {        tension: 0,        borderWidth: 3      }    }  }};    new Chart(ctx, options);</script></p><h3 id="气泡图"><a href="#气泡图" class="headerlink" title="气泡图"></a>气泡图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">&#123;% chart 90% 300 %&#125;</span><br><span class="line">&#123;</span><br><span class="line">  type: &#x27;bubble&#x27;,</span><br><span class="line">  data: &#123;</span><br><span class="line"><span class="code">    datasets: [&#123;</span></span><br><span class="line"><span class="code">      label: &#x27;First Dataset&#x27;,</span></span><br><span class="line"><span class="code">      data: [&#123;</span></span><br><span class="line"><span class="code">        x: 20,</span></span><br><span class="line"><span class="code">        y: 30,</span></span><br><span class="line"><span class="code">        r: 6</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 25,</span></span><br><span class="line"><span class="code">        y: 20,</span></span><br><span class="line"><span class="code">        r: 8</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 32,</span></span><br><span class="line"><span class="code">        y: 6,</span></span><br><span class="line"><span class="code">        r: 7</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 35,</span></span><br><span class="line"><span class="code">        y: 26,</span></span><br><span class="line"><span class="code">        r: 9</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 40,</span></span><br><span class="line"><span class="code">        y: 10,</span></span><br><span class="line"><span class="code">        r: 5</span></span><br><span class="line"><span class="code">      &#125;],</span></span><br><span class="line"><span class="code">      backgroundColor: &#x27;rgb(255, 99, 132)&#x27;</span></span><br><span class="line"><span class="code">    &#125;]</span></span><br><span class="line"><span class="code">  &#125;</span></span><br><span class="line"><span class="code">&#125;</span></span><br><span class="line"><span class="code">&#123;% endchart %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div style="width: 90%;margin: 0 auto">    <canvas id="chart1586" style="height: 300px"></canvas></div><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script><script type="text/javascript">    var ctx = document.getElementById('chart1586').getContext('2d');    var options = {  type: 'bubble',  data: {    datasets: [{      label: 'First Dataset',      data: [{        x: 20,        y: 30,        r: 6      }, {        x: 25,        y: 20,        r: 8      }, {        x: 32,        y: 6,        r: 7      }, {        x: 35,        y: 26,        r: 9      }, {        x: 40,        y: 10,        r: 5      }],      backgroundColor: 'rgb(255, 99, 132)'    }]  }};    new Chart(ctx, options);</script></p><h3 id="散点图"><a href="#散点图" class="headerlink" title="散点图"></a>散点图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line">&#123;% chart 90% 300 %&#125;</span><br><span class="line">&#123;</span><br><span class="line">  type: &#x27;scatter&#x27;,</span><br><span class="line">  data: &#123;</span><br><span class="line"><span class="code">    labels: [&#x27;January&#x27;, &#x27;February&#x27;, &#x27;March&#x27;, &#x27;April&#x27;, &#x27;May&#x27;, &#x27;June&#x27;, &#x27;July&#x27;],</span></span><br><span class="line"><span class="code">    datasets: [&#123;</span></span><br><span class="line"><span class="code">      label: &#x27;First dataset&#x27;,</span></span><br><span class="line"><span class="code">      borderColor: &#x27;rgb(255, 99, 132)&#x27;,</span></span><br><span class="line"><span class="code">      backgroundColor: &#x27;rgb(255, 99, 132)&#x27;,</span></span><br><span class="line"><span class="code">      data: [&#123;</span></span><br><span class="line"><span class="code">        x: 12,</span></span><br><span class="line"><span class="code">        y: 45,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 12,</span></span><br><span class="line"><span class="code">        y: 13,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 56,</span></span><br><span class="line"><span class="code">        y: 3,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 5,</span></span><br><span class="line"><span class="code">        y: 87,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 43,</span></span><br><span class="line"><span class="code">        y: 76,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 34,</span></span><br><span class="line"><span class="code">        y: 8,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 9,</span></span><br><span class="line"><span class="code">        y: 53,</span></span><br><span class="line"><span class="code">      &#125;]</span></span><br><span class="line"><span class="code">    &#125;, &#123;</span></span><br><span class="line"><span class="code">      label: &#x27;Second dataset&#x27;,</span></span><br><span class="line"><span class="code">      borderColor: &#x27;rgb(54, 162, 235)&#x27;,</span></span><br><span class="line"><span class="code">      backgroundColor: &#x27;rgb(54, 162, 235)&#x27;,</span></span><br><span class="line"><span class="code">      data: [&#123;</span></span><br><span class="line"><span class="code">        x: 56,</span></span><br><span class="line"><span class="code">        y: 12,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 7,</span></span><br><span class="line"><span class="code">        y: 12,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 87,</span></span><br><span class="line"><span class="code">        y: 24,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 34,</span></span><br><span class="line"><span class="code">        y: 45,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 65,</span></span><br><span class="line"><span class="code">        y: 27,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 8,</span></span><br><span class="line"><span class="code">        y: 37,</span></span><br><span class="line"><span class="code">      &#125;, &#123;</span></span><br><span class="line"><span class="code">        x: 24,</span></span><br><span class="line"><span class="code">        y: 89,</span></span><br><span class="line"><span class="code">      &#125;]</span></span><br><span class="line"><span class="code">    &#125;],</span></span><br><span class="line"><span class="code">    options: &#123;</span></span><br><span class="line"><span class="code">      title: &#123;</span></span><br><span class="line"><span class="code">        display: true,</span></span><br><span class="line"><span class="code">        text: &#x27;Chart.js Scatter Chart&#x27;</span></span><br><span class="line"><span class="code">      &#125;,</span></span><br><span class="line"><span class="code">    &#125;</span></span><br><span class="line"><span class="code">  &#125;</span></span><br><span class="line"><span class="code">&#125;</span></span><br><span class="line"><span class="code">&#123;% endchart %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div style="width: 90%;margin: 0 auto">    <canvas id="chart5760" style="height: 300px"></canvas></div><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script><script type="text/javascript">    var ctx = document.getElementById('chart5760').getContext('2d');    var options = {  type: 'scatter',  data: {    labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],    datasets: [{      label: 'First dataset',      borderColor: 'rgb(255, 99, 132)',      backgroundColor: 'rgb(255, 99, 132)',      data: [{        x: 12,        y: 45,      }, {        x: 12,        y: 13,      }, {        x: 56,        y: 3,      }, {        x: 5,        y: 87,      }, {        x: 43,        y: 76,      }, {        x: 34,        y: 8,      }, {        x: 9,        y: 53,      }]    }, {      label: 'Second dataset',      borderColor: 'rgb(54, 162, 235)',      backgroundColor: 'rgb(54, 162, 235)',      data: [{        x: 56,        y: 12,      }, {        x: 7,        y: 12,      }, {        x: 87,        y: 24,      }, {        x: 34,        y: 45,      }, {        x: 65,        y: 27,      }, {        x: 8,        y: 37,      }, {        x: 24,        y: 89,      }]    }],    options: {      title: {        display: true,        text: 'Chart.js Scatter Chart'      },    }  }};    new Chart(ctx, options);</script></p><h3 id="混合图"><a href="#混合图" class="headerlink" title="混合图"></a>混合图</h3><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">&#123;% chart 90% 300 %&#125;</span><br><span class="line">&#123;</span><br><span class="line">  type: &#x27;bar&#x27;,</span><br><span class="line">  data: &#123;</span><br><span class="line"><span class="code">    labels: [&#x27;January&#x27;, &#x27;February&#x27;, &#x27;March&#x27;, &#x27;April&#x27;],</span></span><br><span class="line"><span class="code">    datasets: [&#123;</span></span><br><span class="line"><span class="code">      label: &#x27;Bar Dataset&#x27;,</span></span><br><span class="line"><span class="code">      data: [10, 20, 30, 40],</span></span><br><span class="line"><span class="code">      borderColor: &#x27;rgb(255, 99, 132)&#x27;,</span></span><br><span class="line"><span class="code">      backgroundColor: &#x27;rgba(255, 99, 132)&#x27;</span></span><br><span class="line"><span class="code">    &#125;, &#123;</span></span><br><span class="line"><span class="code">      label: &#x27;Line Dataset&#x27;,</span></span><br><span class="line"><span class="code">      data: [20, 30, 40, 50],</span></span><br><span class="line"><span class="code">      type: &#x27;line&#x27;,</span></span><br><span class="line"><span class="code">      fill: false,</span></span><br><span class="line"><span class="code">      borderColor: &#x27;rgb(54, 162, 235)&#x27;</span></span><br><span class="line"><span class="code">    &#125;]</span></span><br><span class="line"><span class="code">  &#125;,</span></span><br><span class="line"><span class="code">  options: &#123;</span></span><br><span class="line"><span class="code">    scales: &#123;</span></span><br><span class="line"><span class="code">      yAxes: [&#123;</span></span><br><span class="line"><span class="code">        ticks: &#123;</span></span><br><span class="line"><span class="code">          beginAtZero: true</span></span><br><span class="line"><span class="code">        &#125;</span></span><br><span class="line"><span class="code">      &#125;]</span></span><br><span class="line"><span class="code">    &#125;</span></span><br><span class="line"><span class="code">  &#125;</span></span><br><span class="line"><span class="code">&#125;</span></span><br><span class="line"><span class="code">&#123;% endchart %&#125;</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div style="width: 90%;margin: 0 auto">    <canvas id="chart8818" style="height: 300px"></canvas></div><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script><script type="text/javascript">    var ctx = document.getElementById('chart8818').getContext('2d');    var options = {  type: 'bar',  data: {    labels: ['January', 'February', 'March', 'April'],    datasets: [{      label: 'Bar Dataset',      data: [10, 20, 30, 40],      borderColor: 'rgb(255, 99, 132)',      backgroundColor: 'rgba(255, 99, 132)'    }, {      label: 'Line Dataset',      data: [20, 30, 40, 50],      type: 'line',      fill: false,      borderColor: 'rgb(54, 162, 235)'    }]  },  options: {    scales: {      yAxes: [{        ticks: {          beginAtZero: true        }      }]    }  }};    new Chart(ctx, options);</script></p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>如果你想了解更多，<a href="https://www.chartjs.org/">官方文档</a> 是不二之选。如果你英语不好，那么可以看看 <a href="https://chartjs.bootcss.com/">中文文档</a>，所有的例子和属性都能在里面找到，祝你玩得开心。</p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      
      <comments>https://blog.eurkon.com/post/a6e75a2b.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Markdown 基本语法</title>
      <link>https://blog.eurkon.com/post/c894e39a.html</link>
      <guid>https://blog.eurkon.com/post/c894e39a.html</guid>
      <pubDate>Sun, 03 Jan 2021 06:14:53 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Markdown-应用&quot;&gt;&lt;a href=&quot;#Markdown-应用&quot; class=&quot;headerlink&quot; title=&quot;Markdown 应用&quot;&gt;&lt;/a&gt;Markdown 应用&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Markdown 是一种轻量级标记语言，它允许人们使用易</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Markdown-应用"><a href="#Markdown-应用" class="headerlink" title="Markdown 应用"></a>Markdown 应用</h2><ul><li>Markdown 是一种轻量级标记语言，它允许人们使用易读易写的纯文本格式编写文档。</li><li>Markdown 语言在 2004 由约翰·格鲁伯（英语：John Gruber）创建。</li><li>Markdown 编写的文档可以导出 HTML、Word、图像、PDF、Epub 等多种格式的文档。</li><li>Markdown 编写的文档后缀为 .md 或 .markdown。</li></ul><p>Markdown 能被使用来撰写电子书，如：Gitbook。</p><p>当前许多网站都广泛使用 Markdown 来撰写帮助文档或是用于论坛上发表消息。例如：GitHub、简书、reddit、Diaspora、Stack Exchange、OpenStreetMap 、SourceForge 等。</p><h2 id="Markdown-标题"><a href="#Markdown-标题" class="headerlink" title="Markdown 标题"></a>Markdown 标题</h2><p>Markdown 标题有两种格式。</p><ol><li><p>使用 <code>=</code> 和 <code>-</code> 标记一级和二级标题</p><p>= 和 - 标记语法格式如下：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">一级标题</span><br><span class="line">=======</span><br><span class="line"></span><br><span class="line">二级标题</span><br><span class="line">-------</span><br></pre></td></tr></table></figure></p><p><!-- 显示效果如下所示：> 一级标题> =======> 二级标题> ------- --></p></li><li><p>使用 <code>#</code> 号标记</p><p>使用 <code>#</code> 号克表示 1-6 级标题，一级标题对应一个 <code>#</code> 号，二级标题对应两个 <code>#</code> 号，以此类推。</p><p> <figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="section"># 一级标题</span></span><br><span class="line"><span class="section">## 二级标题</span></span><br><span class="line"><span class="section">### 三级标题</span></span><br><span class="line"><span class="section">#### 四级标题</span></span><br><span class="line"><span class="section">##### 五级标题</span></span><br><span class="line"><span class="section">###### 六级标题</span></span><br></pre></td></tr></table></figure></p><p> <!-- 显示效果如下所示：> # 一级标题> ## 二级标题> ### 三级标题> #### 四级标题> ##### 五级标题> ###### 六级标题 --></p></li></ol><h2 id="Markdown-段落"><a href="#Markdown-段落" class="headerlink" title="Markdown 段落"></a>Markdown 段落</h2><h3 id="段落"><a href="#段落" class="headerlink" title="段落"></a>段落</h3><p>Markdown 段落没有特殊的格式，直接编写文字就好，段落的换行是使用两个空格以上加上回车。</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Markdown[<span class="string">空格</span>][<span class="symbol">空格</span>][<span class="string">回车</span>]</span><br><span class="line">Markdown</span><br></pre></td></tr></table></figure></p><p>当然也可以在段落后面使用一个空行表示重新开始一个段落</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Markdown</span><br><span class="line">[空行]</span><br><span class="line">Markdown</span><br></pre></td></tr></table></figure></p><p>还可以使用 <code>&lt;br/&gt;</code> 实现强制换行</p><p>显示效果如下所示：</p><blockquote><p>Markdown<br>Markdown</p></blockquote><h3 id="字体"><a href="#字体" class="headerlink" title="字体"></a>字体</h3><p>Markdown 可以使用以下几种字体：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="emphasis">*斜体文本*</span></span><br><span class="line"><span class="emphasis">_斜体文本_</span></span><br><span class="line"></span><br><span class="line"><span class="strong">**粗体文本**</span></span><br><span class="line"><span class="strong">__粗体文本__</span></span><br><span class="line"></span><br><span class="line"><span class="strong">**<span class="emphasis">*斜粗体文本<span class="strong">**<span class="emphasis">*</span></span></span></span></span><br><span class="line"><span class="strong"><span class="emphasis"><span class="strong"><span class="emphasis"><span class="strong">__<span class="emphasis">_斜粗体文本<span class="strong">__<span class="emphasis">_</span></span></span></span></span></span></span></span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p><em>斜体文本</em><br><em>斜体文本</em>  </p><p><strong>粗体文本</strong><br><strong>粗体文本</strong></p><p><strong><em>斜粗体文本</em></strong><br><strong><em>斜粗体文本</em></strong></p></blockquote><h3 id="分隔线"><a href="#分隔线" class="headerlink" title="分隔线"></a>分隔线</h3><p>可以在一行中使用三个以上的星号 <code>*</code>、减号 <code>-</code>、底线 <code>_</code> 来建立一个分隔线，行内不能有其他东西，也可以在星号 <code>*</code>、减号 <code>-</code>、底线 <code>_</code> 中间插入空格。下面每种写法都可以建立分隔线：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line"><span class="strong">**<span class="emphasis">*</span></span></span><br><span class="line"><span class="strong"><span class="emphasis"><span class="strong">__<span class="emphasis">_</span></span></span></span></span><br><span class="line"><span class="strong"><span class="emphasis"><span class="strong"><span class="emphasis">- - -</span></span></span></span></span><br><span class="line"><span class="strong"><span class="emphasis"><span class="strong"><span class="emphasis">* * *</span></span></span></span></span><br><span class="line"><span class="strong"><span class="emphasis"><span class="strong"><span class="emphasis">_</span> <span class="emphasis">_ _</span></span></span></span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><hr><hr><hr><hr><hr><hr></blockquote><h3 id="删除线"><a href="#删除线" class="headerlink" title="删除线"></a>删除线</h3><p>如果段落上的文字要添加删除线，只需要在文字的两端加上两个波浪线 <code>~~</code> 即可：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">正常文本</span><br><span class="line">~~删除文本~~</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p>正常文本<br><del>删除文本</del></p></blockquote><h3 id="下划线"><a href="#下划线" class="headerlink" title="下划线"></a>下划线</h3><p>下划线可以通过 HTML 的 <code>&lt;u&gt;</code> 标签来实现：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">u</span>&gt;</span></span>下划线文本<span class="xml"><span class="tag">&lt;/<span class="name">u</span>&gt;</span></span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p><u>下划线文本</u></p></blockquote><h3 id="脚注"><a href="#脚注" class="headerlink" title="脚注"></a>脚注</h3><p>脚注是对文本的补充说明，Markdown 脚注的格式如下：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[^ 要脚注的文本]</span><br><span class="line">Markdown[^ Markdown]  </span><br><span class="line"></span><br><span class="line">[^ Markdown ]：一种轻量级标记语言</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p>Markdown<sup><a href="#fn_ Markdown" id="reffn_ Markdown"> Markdown</a></sup>。</p><blockquote id="fn_ Markdown"><sup> Markdown</sup>. 一种轻量级标记语言<a href="#reffn_ Markdown" title="Jump back to footnote [ Markdown] in the text."> &#8617;</a></blockquote></blockquote><h2 id="Markdown-列表"><a href="#Markdown-列表" class="headerlink" title="Markdown 列表"></a>Markdown 列表</h2><p>Markdown 支持有序列表和无序列表。</p><p>无序列表使用星号 <code>*</code>、加号 <code>+</code>、减号 <code>-</code> 作为列表标记，这些标记后面都要添加一个空格，然后再填写内容：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">*</span> 第一项</span><br><span class="line"><span class="bullet">*</span> 第二项</span><br><span class="line"><span class="bullet">*</span> 第三项</span><br><span class="line"></span><br><span class="line"><span class="bullet">+</span> 第一项</span><br><span class="line"><span class="bullet">+</span> 第二项</span><br><span class="line"><span class="bullet">+</span> 第三项</span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> 第一项</span><br><span class="line"><span class="bullet">-</span> 第二项</span><br><span class="line"><span class="bullet">-</span> 第三项</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><ul><li>第一项</li><li>第二项</li><li>第三项</li></ul><ul><li>第一项</li><li>第二项</li><li>第三项</li></ul><ul><li>第一项</li><li>第二项</li><li>第三项</li></ul></blockquote><p>有序列表使用数字并加上 <code>.</code> 号来表示，如：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1.</span> 第一项</span><br><span class="line"><span class="bullet">2.</span> 第二项</span><br><span class="line"><span class="bullet">3.</span> 第三项</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><ol><li>第一项</li><li>第二项</li><li>第三项</li></ol></blockquote><p>列表嵌套只需要在子列表中的选项前面添加四个空格即可：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1.</span> 第一项：</span><br><span class="line"><span class="bullet">  -</span> 第一项嵌套的第一个元素</span><br><span class="line"><span class="bullet">  -</span> 第一项嵌套的第二个元素</span><br><span class="line"><span class="bullet">2.</span> 第二项：</span><br><span class="line"><span class="bullet">  1.</span> 第二项嵌套的第一个元素</span><br><span class="line"><span class="bullet">  2.</span> 第二项嵌套的第二个元素</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><ol><li>第一项：<ul><li>第一项嵌套的第一个元素</li><li>第一项嵌套的第二个元素</li></ul></li><li>第二项：<ol><li>第二项嵌套的第一个元素</li><li>第二项嵌套的第二个元素</li></ol></li></ol></blockquote><h2 id="Markdown-区块"><a href="#Markdown-区块" class="headerlink" title="Markdown 区块"></a>Markdown 区块</h2><p>Markdown 区块引用是在段落开头使用 <code>&gt;</code> 符号，然后后面紧跟一个空格符号：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="quote">&gt; 区块引用</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p>区块引用</p></blockquote><p>另外区块是可以嵌套的，一个 <code>&gt;</code> 符号是最外层，两个 <code>&gt;</code> 符号是第一层嵌套，以此类推：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="quote">&gt; 最外层</span></span><br><span class="line"><span class="quote">&gt; &gt; 第一层嵌套</span></span><br><span class="line"><span class="quote">&gt; &gt; &gt; 第二层嵌套</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p>最外层</p><blockquote><p>第一层嵌套</p><blockquote><p>第二层嵌套</p></blockquote></blockquote></blockquote><p><br></p><p><strong>区块中使用列表</strong></p><p>区块中使用列表实例如下：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="quote">&gt; 区块中使用列表</span></span><br><span class="line"><span class="quote">&gt; 1. 第一项</span></span><br><span class="line"><span class="quote">&gt; 2. 第二项</span></span><br><span class="line"><span class="quote">&gt; + 第一项</span></span><br><span class="line"><span class="quote">&gt; + 第二项</span></span><br><span class="line"><span class="quote">&gt; + 第三项</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p>区块中使用列表</p><ol><li>第一项</li><li>第二项</li></ol><ul><li>第一项</li><li>第二项</li><li>第三项</li></ul></blockquote><p><br></p><p><strong>列表中使用区块</strong></p><p>如果要在列表项目内放进区块，那么就需要在 <code>&gt;</code> 前添加四个空格的缩进。</p><p>区块中使用列表实例如下：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">*</span> 第一项</span><br><span class="line"><span class="code">    &gt; 列表中使用区块</span></span><br><span class="line"><span class="code">* 第二项</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><ul><li>第一项<blockquote><p>列表中使用区块</p></blockquote></li><li>第二项</li></ul><h2 id="Markdown-代码"><a href="#Markdown-代码" class="headerlink" title="Markdown 代码"></a>Markdown 代码</h2><p>如果是段落上的一个函数或片段的代码可以用反引号 <code>` </code> 或用三个反引号 <code>``` </code> 把它包起来例如：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="code">` print `</span> 函数</span><br><span class="line"><span class="code">``` print ``` </span>函数</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p><code>print</code> 函数<br><code>print</code> 函数</p></blockquote><p><br></p><p><strong>代码区块</strong></p><p>代码区块使用 4 个空格或者一个制表符（Tab 键）：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">空格</span>][<span class="symbol">空格</span>][<span class="string">空格</span>][<span class="symbol">空格</span>]print(&quot;Hello&quot;);</span><br><span class="line">[Tab]print(&quot;World&quot;);</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p>   print(&quot;Hello&quot;);<br>   print(&quot;World&quot;);</p></blockquote><p>你也可以用 <code>``` </code> 包裹一段代码，并指定一种语言（也可以不指定）：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">```</span>][<span class="symbol">javascript</span>]</span><br><span class="line">$(document).ready(function () &#123;</span><br><span class="line"><span class="code">    alert(&#x27;Hello World&#x27;);</span></span><br><span class="line"><span class="code">&#125;);</span></span><br><span class="line"><span class="code">[```]</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="built_in">document</span>).ready(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    alert(<span class="string">&#x27;Hello World&#x27;</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p></blockquote><h2 id="Markdown-链接"><a href="#Markdown-链接" class="headerlink" title="Markdown 链接"></a>Markdown 链接</h2><p>链接语法格式如下：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">链接名称</span>](<span class="link">链接地址</span>)</span><br><span class="line">或者</span><br><span class="line">&lt;链接地址&gt;</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p><a href="https://www.baidu.com/">Baidu</a><br>或者<br><a href="https://www.baidu.com/">https://www.baidu.com/</a></p></blockquote><p><br></p><p><strong>高级链接</strong></p><p>我们可以通过变量来设置一个链接，变量赋值在文档末尾进行：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">这个链接用 Google 作为网址变量 [<span class="string">Google</span>][<span class="symbol">Google</span>]</span><br><span class="line">然后在文档的结尾为变量赋值（网址）</span><br><span class="line">[<span class="symbol">Google</span>]: <span class="link">http://www.google.com/</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p>这个链接用 Google 作为网址变量 [Google][Google]<br>然后在文档的结尾为变量赋值（网址）</p><p>  [Google]: <a href="http://www.google.com/">http://www.google.com/</a></p></blockquote><h2 id="Markdown-图片"><a href="#Markdown-图片" class="headerlink" title="Markdown 图片"></a>Markdown 图片</h2><p>Markdown 图片语法格式如下：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">![<span class="string">alt 属性文本</span>](<span class="link">图片地址</span>)</span><br><span class="line">![<span class="string">alt 属性文本</span>](<span class="link">图片地址 &quot;可选标题&quot;</span>)</span><br></pre></td></tr></table></figure></p><ul><li>开头一个感叹号 <code>!</code></li><li>接着一个方括号，里面放上图片的替代文字</li><li>接着一个普通括号，里面放上图片的网址，最后还可以用引号包住并加上选择性的 <code>title</code> 属性的文字。</li></ul><p>使用实例：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">![<span class="string">图标</span>](<span class="link">https://www.baidu.com/img/flexible/logo/pc/result.png</span>)</span><br><span class="line">![<span class="string">图标</span>](<span class="link">https://www.baidu.com/img/flexible/logo/pc/result.png &quot;Baidu&quot;</span>)</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://www.baidu.com/img/flexible/logo/pc/result.png" alt="图标"><br><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://www.baidu.com/img/flexible/logo/pc/result.png" alt="图标" title="Baidu"></p></blockquote><p>当然，也可以像网址那样对图片网址使用变量:</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">这个链接用 Baidu 作为网址变量 [<span class="string">Baidu</span>][<span class="symbol">Baidu</span>]。</span><br><span class="line">然后在文档的结尾为变量赋值（网址）</span><br><span class="line"></span><br><span class="line">[<span class="symbol">Baidu</span>]: <span class="link">https://www.baidu.com/img/flexible/logo/pc/result.png</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p>这个链接用 Baidu 作为网址变量 [Baidu][Baidu]。<br>然后在文档的结尾为变量赋值（网址）</p><p>[Baidu]: <a href="https://www.baidu.com/img/flexible/logo/pc/result.png">https://www.baidu.com/img/flexible/logo/pc/result.png</a></p></blockquote><p>Markdown 还没有办法指定图片的高度与宽度，如果你需要的话，你可以使用普通的 <code>&lt;img&gt;</code> 标签。</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;https://www.baidu.com/img/flexible/logo/pc/result.png&quot;</span> <span class="attr">width</span>=<span class="string">&quot;30%&quot;</span>&gt;</span></span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p><img src="https://npm.elemecdn.com/eurkon-cdn/hexo/images/user/loading.gif" data-lazy-src="https://www.baidu.com/img/flexible/logo/pc/result.png" width="30%"></p></blockquote><h2 id="Markdown-表格"><a href="#Markdown-表格" class="headerlink" title="Markdown 表格"></a>Markdown 表格</h2><p>Markdown 制作表格使用 <code>|</code> 来分隔不同的单元格，使用 <code>-</code> 来分隔表头和其他行。</p><p>语法格式如下：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">| 表头 | 表头 |</span><br><span class="line">| ---- | ---- |</span><br><span class="line">| 单元格 | 单元格 |</span><br><span class="line">| 单元格 | 单元格 |</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><div class="table-container"><table><thead><tr><th>表头</th><th>表头</th></tr></thead><tbody><tr><td>单元格</td><td>单元格</td></tr><tr><td>单元格</td><td>单元格</td></tr></tbody></table></div></blockquote><p><br></p><p><strong>对齐方式</strong></p><p>我们可以设置表格的对齐方式：</p><ul><li><code>-:</code> 设置内容和标题栏居右对齐。</li><li><code>:-</code> 设置内容和标题栏居左对齐。</li><li><code>:-:</code> 设置内容和标题栏居中对齐。</li></ul><p>实例如下：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">| 左对齐 | 居中对齐 | 右对齐 |</span><br><span class="line">| :-----| :----: | ----: |</span><br><span class="line">| 单元格 | 单元格 | 单元格 |</span><br><span class="line">| 单元格 | 单元格 | 单元格 |</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><div class="table-container"><table><thead><tr><th style="text-align:left">左对齐</th><th style="text-align:center">居中对齐</th><th style="text-align:right">右对齐</th></tr></thead><tbody><tr><td style="text-align:left">单元格</td><td style="text-align:center">单元格</td><td style="text-align:right">单元格</td></tr><tr><td style="text-align:left">单元格</td><td style="text-align:center">单元格</td><td style="text-align:right">单元格</td></tr></tbody></table></div></blockquote><h2 id="Markdown-高级技巧"><a href="#Markdown-高级技巧" class="headerlink" title="Markdown 高级技巧"></a>Markdown 高级技巧</h2><h3 id="支持的-HTML-元素"><a href="#支持的-HTML-元素" class="headerlink" title="支持的 HTML 元素"></a>支持的 HTML 元素</h3><p>不在 Markdown 涵盖范围之内的标签，都可以直接在文档里面用 HTML 撰写。</p><p>目前支持的 HTML 元素有：<code>&lt;kbd&gt;</code> <code>&lt;b&gt;</code> <code>&lt;i&gt;</code> <code>&lt;em&gt;</code> <code>&lt;sup&gt;</code> <code>&lt;sub&gt;</code> <code>&lt;br&gt;</code> 等 ，如：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">普通文本：使用 Ctrl+Alt+Del 重启电脑</span><br><span class="line">HTML 文本：使用 <span class="xml"><span class="tag">&lt;<span class="name">kbd</span>&gt;</span></span>Ctrl<span class="xml"><span class="tag">&lt;/<span class="name">kbd</span>&gt;</span></span>+<span class="xml"><span class="tag">&lt;<span class="name">kbd</span>&gt;</span></span>Alt<span class="xml"><span class="tag">&lt;/<span class="name">kbd</span>&gt;</span></span>+<span class="xml"><span class="tag">&lt;<span class="name">kbd</span>&gt;</span></span>Del<span class="xml"><span class="tag">&lt;/<span class="name">kbd</span>&gt;</span></span> 重启电脑</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p>普通文本：使用 Ctrl+Alt+Del 重启电脑<br>HTML 文本：使用 <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Del</kbd> 重启电脑</p></blockquote><h3 id="转义"><a href="#转义" class="headerlink" title="转义"></a>转义</h3><p>Markdown 使用了很多特殊符号来表示特定的意义，如果需要显示特定的符号则需要使用转义字符，Markdown 使用反斜杠转义特殊字符：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="strong">**文本加粗**</span> </span><br><span class="line">\<span class="emphasis">*\*</span> 正常显示星号 \<span class="emphasis">*\*</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><p><strong>文本加粗</strong><br>** 正常显示星号 **</p></blockquote><p>Markdown 支持以下这些符号前面加上反斜杠来帮助插入普通的符号：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">\   反斜线</span><br><span class="line">`   反引号</span><br><span class="line"><span class="bullet">*</span>   星号</span><br><span class="line"><span class="emphasis">_   下划线</span></span><br><span class="line"><span class="emphasis">&#123;&#125;  花括号</span></span><br><span class="line"><span class="emphasis">[]  方括号</span></span><br><span class="line"><span class="emphasis">()  小括号</span></span><br><span class="line"><span class="emphasis">#   井字号</span></span><br><span class="line"><span class="emphasis">+   加号</span></span><br><span class="line"><span class="emphasis">-   减号</span></span><br><span class="line"><span class="emphasis">.   英文句点</span></span><br><span class="line"><span class="emphasis">!   感叹号</span></span><br></pre></td></tr></table></figure></p><h3 id="公式"><a href="#公式" class="headerlink" title="公式"></a>公式</h3><p>当你需要在编辑器中插入数学公式时，可以使用两个美元符号<code>$$</code>包裹 <code>TeX</code> 或 <code>LaTeX</code> 格式的数学公式来实现。提交后，问答和文章页会根据需要加载 <code>Mathjax</code> 对数学公式进行渲染。如：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$$</span><br><span class="line">\mathbf&#123;V&#125;<span class="emphasis">_1 \times \mathbf&#123;V&#125;_</span>2 =  \begin&#123;vmatrix&#125; </span><br><span class="line">\mathbf&#123;i&#125; &amp; \mathbf&#123;j&#125; &amp; \mathbf&#123;k&#125; \\</span><br><span class="line">\frac&#123;\partial X&#125;&#123;\partial u&#125; &amp;  \frac&#123;\partial Y&#125;&#123;\partial u&#125; &amp; 0 \\</span><br><span class="line">\frac&#123;\partial X&#125;&#123;\partial v&#125; &amp;  \frac&#123;\partial Y&#125;&#123;\partial v&#125; &amp; 0 \\</span><br><span class="line">\end&#123;vmatrix&#125;</span><br><span class="line">$&#123;$tep1&#125;&#123;\style&#123;visibility:hidden&#125;&#123;(x+1)(x+1)&#125;&#125;</span><br><span class="line">$$</span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><blockquote><script type="math/tex; mode=display">\mathbf{V}_1 \times \mathbf{V}_2 =  \begin{vmatrix} \mathbf{i} & \mathbf{j} & \mathbf{k} \\\frac{\partial X}{\partial u} &  \frac{\partial Y}{\partial u} & 0 \\\frac{\partial X}{\partial v} &  \frac{\partial Y}{\partial v} & 0 \\\end{vmatrix}${$tep1}{\style{visibility:hidden}{(x+1)(x+1)}}</script></blockquote><h3 id="流程图"><a href="#流程图" class="headerlink" title="流程图"></a>流程图</h3><ol><li><p>横向流程图源码格式：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">```</span>][<span class="symbol">mermaid</span>]</span><br><span class="line">graph LR</span><br><span class="line">A[方形] --&gt;B(圆角)</span><br><span class="line"><span class="code">    B --&gt; C&#123;条件 a&#125;</span></span><br><span class="line"><span class="code">    C --&gt;|a=1| D[结果 1]</span></span><br><span class="line"><span class="code">    C --&gt;|a=2| E[结果 2]</span></span><br><span class="line"><span class="code">    F[横向流程图]</span></span><br><span class="line"><span class="code">[```]</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><pre class="mermaid">  graph LRA[方形] -->B(圆角)   B --> C{条件 a}   C -->|a=1| D[结果 1]   C -->|a=2| E[结果 2]   F[横向流程图]</pre></p></li><li><p>竖向流程图源码格式：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">```</span>][<span class="symbol">mermaid</span>]</span><br><span class="line">graph TD</span><br><span class="line">A[方形] --&gt; B(圆角)</span><br><span class="line"><span class="code">    B --&gt; C&#123;条件 a&#125;</span></span><br><span class="line"><span class="code">    C --&gt; |a=1| D[结果 1]</span></span><br><span class="line"><span class="code">    C --&gt; |a=2| E[结果 2]</span></span><br><span class="line"><span class="code">    F[竖向流程图]</span></span><br><span class="line"><span class="code">[```]</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><pre class="mermaid">  graph TDA[方形] --> B(圆角)   B --> C{条件 a}   C --> |a=1| D[结果 1]   C --> |a=2| E[结果 2]   F[竖向流程图]</pre></p></li><li><p>标准流程图源码格式：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">```</span>][<span class="symbol">flow</span>]</span><br><span class="line">st=&gt;start: 开始框</span><br><span class="line">op=&gt;operation: 处理框</span><br><span class="line">cond=&gt;condition: 判断框(是或否?)</span><br><span class="line">sub1=&gt;subroutine: 子流程</span><br><span class="line">io=&gt;inputoutput: 输入输出框</span><br><span class="line">e=&gt;end: 结束框</span><br><span class="line">st-&gt;op-&gt;cond</span><br><span class="line">cond(yes)-&gt;io-&gt;e</span><br><span class="line">cond(no)-&gt;sub1(right)-&gt;op</span><br><span class="line">[<span class="code">```]</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="flowchart-0" class="flow-chart"></div></p></li><li><p>标准流程图源码格式（横向）：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">```</span>][<span class="symbol">flow</span>]</span><br><span class="line">st=&gt;start: 开始框</span><br><span class="line">op=&gt;operation: 处理框</span><br><span class="line">cond=&gt;condition: 判断框(是或否?)</span><br><span class="line">sub1=&gt;subroutine: 子流程</span><br><span class="line">io=&gt;inputoutput: 输入输出框</span><br><span class="line">e=&gt;end: 结束框</span><br><span class="line">st(right)-&gt;op(right)-&gt;cond</span><br><span class="line">cond(yes)-&gt;io(bottom)-&gt;e</span><br><span class="line">cond(no)-&gt;sub1(right)-&gt;op</span><br><span class="line">[<span class="code">```]</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="flowchart-1" class="flow-chart"></div></p></li></ol><h3 id="时序图-顺序图"><a href="#时序图-顺序图" class="headerlink" title="时序图(顺序图)"></a>时序图(顺序图)</h3><ol><li><p>UML 时序图源码样例：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">```</span>][<span class="symbol">sequence</span>]</span><br><span class="line">对象 A-&gt;对象 B: 对象 B 你好吗?（请求）</span><br><span class="line">Note right of 对象 B: 对象 B 的描述</span><br><span class="line">Note left of 对象 A: 对象 A 的描述(提示)</span><br><span class="line">对象 B--&gt;对象 A: 我很好(响应)</span><br><span class="line">对象 A-&gt;对象 B: 你真的好吗？</span><br><span class="line">[<span class="code">```]</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="sequence-0"></div></p></li><li><p>UML 时序图源码复杂样例：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">```</span>][<span class="symbol">sequence</span>]</span><br><span class="line">Title: 标题：复杂使用</span><br><span class="line">对象 A-&gt;对象 B: 对象 B 你好吗?（请求）</span><br><span class="line">Note right of 对象 B: 对象 B 的描述</span><br><span class="line">Note left of 对象 A: 对象 A 的描述(提示)</span><br><span class="line">对象 B--&gt;对象 A: 我很好(响应)</span><br><span class="line">对象 B-&gt;小三: 你好吗</span><br><span class="line">小三--&gt;&gt;对象 A: 对象 B 找我了</span><br><span class="line">对象 A-&gt;对象 B: 你真的好吗？</span><br><span class="line">Note over 小三, 对象 B: 我们是朋友</span><br><span class="line">participant C</span><br><span class="line">Note right of C: 没人陪我玩</span><br><span class="line">[<span class="code">```]</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><div id="sequence-1"></div></p></li><li><p>UML 标准时序图样例：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">```</span>][<span class="symbol">mermaid</span>]</span><br><span class="line">%% 时序图例子，-&gt; 直线，--&gt;虚线，-&gt;&gt;实线箭头</span><br><span class="line">  sequenceDiagram</span><br><span class="line"><span class="code">    participant 张三</span></span><br><span class="line"><span class="code">    participant 李四</span></span><br><span class="line"><span class="code">    张三-&gt;王五: 王五你好吗？</span></span><br><span class="line"><span class="code">    loop 健康检查</span></span><br><span class="line"><span class="code">        王五-&gt;王五: 与疾病战斗</span></span><br><span class="line"><span class="code">    end</span></span><br><span class="line"><span class="code">    Note right of 王五: 合理 食物 &lt;br/&gt;看医生...</span></span><br><span class="line"><span class="code">    李四--&gt;&gt;张三: 很好!</span></span><br><span class="line"><span class="code">    王五-&gt;李四: 你怎么样?</span></span><br><span class="line"><span class="code">    李四--&gt;王五: 很好!</span></span><br><span class="line"><span class="code">[```]</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><pre class="mermaid">  %% 时序图例子，-> 直线，-->虚线，->>实线箭头 sequenceDiagram   participant 张三   participant 李四   张三->王五: 王五你好吗？   loop 健康检查       王五->王五: 与疾病战斗   end   Note right of 王五: 合理 食物 <br>看医生...   李四-->>张三: 很好!   王五->李四: 你怎么样?   李四-->王五: 很好!</pre></p></li></ol><h3 id="甘特图"><a href="#甘特图" class="headerlink" title="甘特图"></a>甘特图</h3><p>甘特图样例：</p><p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">```</span>][<span class="symbol">mermaid</span>]</span><br><span class="line">%% 语法示例</span><br><span class="line">  gantt</span><br><span class="line">  dateFormat YYYY-MM-DD</span><br><span class="line">  title 软件开发甘特图</span><br><span class="line">  section 设计</span><br><span class="line"><span class="code">    需求: done, des1, 2014-01-06, 2014-01-08</span></span><br><span class="line"><span class="code">    原型: active, des2, 2014-01-09, 3d</span></span><br><span class="line"><span class="code">    UI 设计: des3, after des2, 5d</span></span><br><span class="line"><span class="code">    未来任务: des4, after des3, 5d</span></span><br><span class="line"><span class="code">  section 开发</span></span><br><span class="line"><span class="code">    理解需求: crit, done, 2014-01-06, 24h</span></span><br><span class="line"><span class="code">    设计框架: crit, done, after des2, 2d</span></span><br><span class="line"><span class="code">    开发: crit, active, 3d</span></span><br><span class="line"><span class="code">    未来任务: crit, 5d</span></span><br><span class="line"><span class="code">    玩耍: 2d</span></span><br><span class="line"><span class="code">  section 测试</span></span><br><span class="line"><span class="code">    功能测试: active, a1, after des3, 3d</span></span><br><span class="line"><span class="code">    压力测试: after a1, 20h</span></span><br><span class="line"><span class="code">    测试报告: 48h</span></span><br><span class="line"><span class="code">[```]</span></span><br></pre></td></tr></table></figure></p><p>显示效果如下所示：</p><p><code>mermaid%% 语法示例  gantt  dateFormat YYYY-MM-DD  title 软件开发甘特图  section 设计    需求: done, des1, 2014-01-06, 2014-01-08    原型: active, des2, 2014-01-09, 3d    UI 设计: des3, after des2, 5d    未来任务: des4, after des3, 5d  section 开发    理解需求: crit, done, 2014-01-06, 24h    设计框架: crit, done, after des2, 2d    开发: crit, active, 3d    未来任务: crit, 5d    玩耍: 2d  section 测试    功能测试: active, a1, after des3, 3d    压力测试: after a1, 20h    测试报告: 48h</code><script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.2.7/raphael.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/flowchart/1.6.5/flowchart.min.js"></script><textarea id="flowchart-0-code" style="display: none">  st=>start: 开始框  op=>operation: 处理框  cond=>condition: 判断框(是或否?)  sub1=>subroutine: 子流程  io=>inputoutput: 输入输出框  e=>end: 结束框  st->op->cond  cond(yes)->io->e  cond(no)->sub1(right)->op</textarea><textarea id="flowchart-0-options" style="display: none">{"theme":"simple","scale":1,"line-width":2,"line-length":50,"text-margin":10,"font-size":12}</textarea><script>  var code = document.getElementById("flowchart-0-code").value;  var options = JSON.parse(decodeURIComponent(document.getElementById("flowchart-0-options").value));  var diagram = flowchart.parse(code);  diagram.drawSVG("flowchart-0", options);</script><textarea id="flowchart-1-code" style="display: none">  st=>start: 开始框  op=>operation: 处理框  cond=>condition: 判断框(是或否?)  sub1=>subroutine: 子流程  io=>inputoutput: 输入输出框  e=>end: 结束框  st(right)->op(right)->cond  cond(yes)->io(bottom)->e  cond(no)->sub1(right)->op</textarea><textarea id="flowchart-1-options" style="display: none">{"theme":"simple","scale":1,"line-width":2,"line-length":50,"text-margin":10,"font-size":12}</textarea><script>  var code = document.getElementById("flowchart-1-code").value;  var options = JSON.parse(decodeURIComponent(document.getElementById("flowchart-1-options").value));  var diagram = flowchart.parse(code);  diagram.drawSVG("flowchart-1", options);</script><script src="https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.27/webfontloader.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/js-sequence-diagrams/1.0.6/sequence-diagram-min.js"></script><textarea id="sequence-0-code" style="display: none">  对象 A->对象 B: 对象 B 你好吗?（请求）  Note right of 对象 B: 对象 B 的描述  Note left of 对象 A: 对象 A 的描述(提示)  对象 B-->对象 A: 我很好(响应)  对象 A->对象 B: 你真的好吗？</textarea><textarea id="sequence-0-options" style="display: none">{"theme":"simple","scale":1,"line-width":2,"line-length":50,"text-margin":10,"font-size":12}</textarea><script>  var code = document.getElementById("sequence-0-code").value;  var options = JSON.parse(decodeURIComponent(document.getElementById("sequence-0-options").value));  var diagram = Diagram.parse(code);  diagram.drawSVG("sequence-0", options);</script><textarea id="sequence-1-code" style="display: none">  Title: 标题：复杂使用  对象 A->对象 B: 对象 B 你好吗?（请求）  Note right of 对象 B: 对象 B 的描述  Note left of 对象 A: 对象 A 的描述(提示)  对象 B-->对象 A: 我很好(响应)  对象 B->小三: 你好吗  小三-->>对象 A: 对象 B 找我了  对象 A->对象 B: 你真的好吗？  Note over 小三, 对象 B: 我们是朋友  participant C  Note right of C: 没人陪我玩</textarea><textarea id="sequence-1-options" style="display: none">{"theme":"simple","scale":1,"line-width":2,"line-length":50,"text-margin":10,"font-size":12}</textarea><script>  var code = document.getElementById("sequence-1-code").value;  var options = JSON.parse(decodeURIComponent(document.getElementById("sequence-1-options").value));  var diagram = Diagram.parse(code);  diagram.drawSVG("sequence-1", options);</script></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Markdown/">Markdown</category>
      
      
      <comments>https://blog.eurkon.com/post/c894e39a.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Front-matter</title>
      <link>https://blog.eurkon.com/post/31e4c77c.html</link>
      <guid>https://blog.eurkon.com/post/31e4c77c.html</guid>
      <pubDate>Sat, 02 Jan 2021 13:59:56 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Page-Front-matter&quot;&gt;&lt;a href=&quot;#Page-Front-matter&quot; class=&quot;headerlink&quot; title=&quot;Page Front-matter&quot;&gt;&lt;/a&gt;Page Front-matter&lt;/h2&gt;&lt;div class=&quot;t</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Page-Front-matter"><a href="#Page-Front-matter" class="headerlink" title="Page Front-matter"></a>Page Front-matter</h2><div class="table-container"><table><thead><tr><th>写法</th><th>解释</th></tr></thead><tbody><tr><td>title</td><td>【必需】页面标题</td></tr><tr><td>date</td><td>【必需】页面创建日期</td></tr><tr><td>type</td><td>【必需】标签、分类和友情链接三个页面需要配置</td></tr><tr><td>updated</td><td>【必需】标签、分类和友情链接三个页面需要配置</td></tr><tr><td>description</td><td>【可选】页面描述</td></tr><tr><td>keywords</td><td>【可选】页面关键字</td></tr><tr><td>comments</td><td>【可选】显示页面评论模块 (默认 true)</td></tr><tr><td>top_img</td><td>【可选】页面顶部图片</td></tr><tr><td>mathjax</td><td>【可选】显示 mathjax (当设置 mathjax 的 per_page: false 时，才需要配置，默认 false)</td></tr><tr><td>katex</td><td>【可选】显示 katex (当设置 katex 的 per_page: false 时，才需要配置，默认 false)</td></tr><tr><td>aside</td><td>【可选】显示侧边栏 (默认 true)</td></tr><tr><td>aplayer</td><td>【可选】在需要的页面加载 aplayer 的 js 和 css</td></tr><tr><td>highlight_shrink</td><td>【可选】配置代码框是否展开 (true/false) (默认为设置中 highlight_shrink 的配置)</td></tr></tbody></table></div><h2 id="Post-Front-matter"><a href="#Post-Front-matter" class="headerlink" title="Post Front-matter"></a>Post Front-matter</h2><div class="table-container"><table><thead><tr><th>写法</th><th>解释</th></tr></thead><tbody><tr><td>title</td><td>【必需】文章标题</td></tr><tr><td>date</td><td>【必需】文章创建日期</td></tr><tr><td>updated</td><td>【可选】文章更新日期</td></tr><tr><td>tags</td><td>【可选】文章标签</td></tr><tr><td>categories</td><td>【可选】文章分类</td></tr><tr><td>keywords</td><td>【可选】文章关键字</td></tr><tr><td>description</td><td>【可选】文章描述</td></tr><tr><td>top_img</td><td>【可选】文章顶部图片</td></tr><tr><td>cover</td><td>【可选】文章缩略图 (如果没有设置 top_img，文章页顶部将显示缩略图，可设为 false/图片地址/留空)</td></tr><tr><td>comments</td><td>【可选】显示文章评论模块 (默认 true)</td></tr><tr><td>toc</td><td>【可选】显示文章 TOC (默认为设置中 toc 的 enable 配置)</td></tr><tr><td>toc_number</td><td>【可选】显示 toc_number (默认为设置中 toc 的 number 配置)</td></tr><tr><td>copyright</td><td>【可选】显示文章版权模块 (默认为设置中 post_copyright 的 enable 配置)</td></tr><tr><td>copyright_author</td><td>【可选】文章版权模块的文章作者</td></tr><tr><td>copyright_author_href</td><td>【可选】文章版权模块的文章作者链接</td></tr><tr><td>copyright_url</td><td>【可选】文章版权模块的文章版权链接</td></tr><tr><td>copyright_info</td><td>【可选】文章版权模块的版权声明文字</td></tr><tr><td>mathjax</td><td>【可选】显示 mathjax (当设置 mathjax 的 per_page: false 时，才需要配置，默认 false)</td></tr><tr><td>katex</td><td>【可选】显示 katex (当设置 katex 的 per_page: false 时，才需要配置，默认 false)</td></tr><tr><td>aplayer</td><td>【可选】在需要的页面加载 aplayer 的 js 和 css</td></tr><tr><td>highlight_shrink</td><td>【可选】配置代码框是否展开 (true/false) (默认为设置中 highlight_shrink 的配置)</td></tr><tr><td>aside</td><td>【可选】显示侧边栏 (默认 true)</td></tr></tbody></table></div>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      <category domain="https://blog.eurkon.com/tags/Butterfly/">Butterfly</category>
      
      
      <comments>https://blog.eurkon.com/post/31e4c77c.html#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Hello Hexo</title>
      <link>https://blog.eurkon.com/post/a1751c09.html</link>
      <guid>https://blog.eurkon.com/post/a1751c09.html</guid>
      <pubDate>Fri, 01 Jan 2021 00:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;Welcome to &lt;a href=&quot;https://hexo.io/&quot;&gt;Hexo&lt;/a&gt;! This is your very first post. Check &lt;a href=&quot;https://hexo.io/docs/&quot;&gt;documentation&lt;/a&gt; for</description>
        
      
      
      
      <content:encoded><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> hexo new <span class="string">&quot;My New Post&quot;</span></span></span><br></pre></td></tr></table></figure></p><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> hexo server</span></span><br></pre></td></tr></table></figure></p><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> hexo generate</span></span><br></pre></td></tr></table></figure></p><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> hexo deploy</span></span><br></pre></td></tr></table></figure></p><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content:encoded>
      
      
      <category domain="https://blog.eurkon.com/categories/%E9%AD%94%E6%94%B9%E6%95%99%E7%A8%8B/">魔改教程</category>
      
      
      <category domain="https://blog.eurkon.com/tags/Hexo/">Hexo</category>
      
      
      <comments>https://blog.eurkon.com/post/a1751c09.html#disqus_thread</comments>
      
    </item>
    
  </channel>
</rss>
