<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="https://yangtiancoder.github.io/atom.xml" rel="self" type="application/atom+xml" /><link href="https://yangtiancoder.github.io/" rel="alternate" type="text/html" /><updated>2020-12-28T06:00:04+00:00</updated><id>https://yangtiancoder.github.io/atom.xml</id><title type="html">Yangtian’s blog site</title><subtitle>你来啦。</subtitle><author><name>Yangtian</name></author><entry><title type="html">记录单条HQL语句配置调优过程</title><link href="https://yangtiancoder.github.io/blog/2019/02/17/hadoop-hive-impro/" rel="alternate" type="text/html" title="记录单条HQL语句配置调优过程" /><published>2019-02-17T00:00:00+00:00</published><updated>2019-02-17T00:00:00+00:00</updated><id>https://yangtiancoder.github.io/blog/2019/02/17/hadoop-hive-impro</id><content type="html" xml:base="https://yangtiancoder.github.io/blog/2019/02/17/hadoop-hive-impro/">&lt;p&gt;前言：数据库调优的PJ,我完成的实验部分内容包括基准测试，和一系列调优过程，因此在此记录一下调优过程，具体实验基准测试数据就不放在这里了，需要的请与我联系。本次测试基于hadoopHA环境基础上，使用mysql+hive对选取的数据集（TPC-H提供的GB级标准测试数据）进行性能测试，按照目前流行的TPC-H测试方法，进行了各种标准案例的测试，并在此基础上，进行了Hive配置参数，进行时间优化。&lt;/p&gt;

&lt;p&gt;主要工具：
CentOS release 6.4
hadoop-2.5.0
hive-1.2.2
TPC-H_on_Hive
tpc-h
配置：
三台虚拟机，其中，两台2GB内存20GB硬盘，一台4GB内存20GB硬盘&lt;/p&gt;

&lt;h2 id=&quot;tpc-h&quot;&gt;tpc-h&lt;/h2&gt;
&lt;p&gt;TPC-H基准测试包括22个查询（Q1~Q22）随机组成查询流
TPC-H模型的评价指标主要为各个查询的响应时间，即从提交查询到结果返回所需时间。TPC-H基准测试的度量单位：每小时执行的查询数（Qph@size）。其中H表示每小时系统执行复杂查询的平均次数，size表示数据库规模的大小，它能够反映出系统在处理查询时的能力。
TPC-H模型的表结构，8张表（表上有些约束等需要满足，参见TPC-H规范），如下：
PART：表示零件的信息
SUPPLIER：表示供货商的信息
PARTSUPP：表示供货商的零件的信息
CUSTOMER：表示消费者的信息
ORDERS：表示订单的信息
LINEITEM：表示订单的信息
NATION：表示国家的信息
REGION：表示地区的信息&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/hiveim-1.png?raw=true&quot; alt=&quot;&quot; /&gt;
TPC-H表结构图&lt;/p&gt;

&lt;h2 id=&quot;调优过程&quot;&gt;调优过程&lt;/h2&gt;

&lt;p&gt;q4语句：
DROP TABLE orders;&lt;br /&gt;
DROP TABLE lineitem;&lt;br /&gt;
DROP TABLE q4_order_priority_tmp;&lt;br /&gt;
DROP TABLE q4_order_priority;&lt;br /&gt;
– create tables and load data
create external table orders (O_ORDERKEY INT, O_CUSTKEY INT, O_ORDERSTATUS STRING, O_TOTALPRICE DOUBLE, O_ORDERDATE STRING, O_ORDERPRIORITY STRING, O_CLERK STRING, O_SHIPPRIORITY INT, O_COMMENT STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘|’ STORED AS TEXTFILE LOCATION ‘/tpch/orders’;&lt;br /&gt;
Create external table lineitem (L_ORDERKEY INT, L_PARTKEY INT, L_SUPPKEY INT, L_LINENUMBER INT, L_QUANTITY DOUBLE, L_EXTENDEDPRICE DOUBLE, L_DISCOUNT DOUBLE, L_TAX DOUBLE, L_RETURNFLAG STRING, L_LINESTATUS STRING, L_SHIPDATE STRING, L_COMMITDATE STRING, L_RECEIPTDATE STRING, L_SHIPINSTRUCT STRING, L_SHIPMODE STRING, L_COMMENT STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘|’ STORED AS TEXTFILE LOCATION ‘/tpch/lineitem’;&lt;br /&gt;
– create the target table
CREATE TABLE q4_order_priority_tmp (O_ORDERKEY INT);&lt;br /&gt;
CREATE TABLE q4_order_priority (O_ORDERPRIORITY STRING, ORDER_COUNT INT);&lt;br /&gt;
set mapred.min.split.size=536870912;&lt;br /&gt;
– the query
INSERT OVERWRITE TABLE q4_order_priority_tmp &lt;br /&gt;
select &lt;br /&gt;
  DISTINCT l_orderkey &lt;br /&gt;
from &lt;br /&gt;
  lineitem 
where &lt;br /&gt;
  l_commitdate &amp;lt; l_receiptdate;&lt;br /&gt;
INSERT OVERWRITE TABLE q4_order_priority &lt;br /&gt;
select o_orderpriority, count(1) as order_count &lt;br /&gt;
from &lt;br /&gt;
  orders o join q4_order_priority_tmp t &lt;br /&gt;
  on &lt;br /&gt;
o.o_orderkey = t.o_orderkey and o.o_orderdate &amp;gt;= ‘1993-07-01’ and o.o_orderdate &amp;lt; ‘1993-10-01’ &lt;br /&gt;
group by o_orderpriority &lt;br /&gt;
order by o_orderpriority;&lt;/p&gt;

&lt;p&gt;我们以q4查询为例，逐步优化，最后总体测试优化结果;&lt;/p&gt;

&lt;p&gt;default配置下q4查询总时间为：
Time taken:216.158&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;优化1：部分本地模式&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;property&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;hive.exec.mode.local.auto&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Let Hive determine whether to run in local mode automatically&lt;span class=&quot;nt&quot;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;第一项优化我们开启了本地模式，再次运行，得到结果，
Time taken:210.367
说明优化产生作用，但是效果并不明显，我们在输出日志注意到如下内容：&lt;/p&gt;
&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Cannot run job locally: Input Size (= 759863287) is larger than hive.exec.mode.local.auto.inputbytes.max (= 134217728)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;说明本地模式并不完全，有部分作业超过本地内存输入，因此不在本地进行，因此我们进行第二项优化。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;优化2：本地模式完全开启&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;hive.exec.mode.local.auto.inputbytes.max&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;760000000&lt;span class=&quot;nt&quot;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;When hive.exec.mode.local.auto is true, input bytes should less than this for local mode.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Time taken:67.465
可见优化效果明显。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;优化3：开启并行&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;property&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;hive.exec.parallel&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Time taken:76.007 seconds
发现时间并没有缩短反而延长，多次测试后发现不是偶然现象，优化没有效果，查阅资料可能是本地没有足够资源进行并行，加上部分查询本身存在依赖，因此本次优化并没有取得很好的效果，除此之外在完全本地模式下，本地资源已经造成足够大的负担,采用其他优化在此之上时间并没有缩短，因此之后的优化中取消完全本地模式，开启半本地模式，其他任务还是提交到集群中进行处理。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;优化4：groupby优化&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;property&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;hive.groupby.skewindata&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Whether there is skew in data to optimize group by queries&lt;span class=&quot;nt&quot;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;totaljob=8
Time taken:200.623
我们发现输出日志内容中，job数目从7增加到8，groupby配置用于控制负载均衡，当数据出现倾斜时，如果该变量设置为true，那么Hive会自动进行负载均衡。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;优化5：非严格分区模式&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;property&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;hive.exec.dynamic.partition.mode&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;nonstrict&lt;span class=&quot;nt&quot;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;description&amp;gt;&lt;/span&gt;
      In strict mode, the user must specify at least one static partition
      in case the user accidentally overwrites all partitions.
      In nonstrict mode all partitions are allowed to be dynamic.
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;hive提供了一个严格模式，可以防止用户执行那些可能产生意想不到的不好的效果的查询。即某些查询在严格
模式下无法执行。通过设置hive.mapred.mode的值为strict，可以禁止3种类型的查询。修改配置之后，发现测试在时间运行上没有优化效果
Time taken:202.823 seconds&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;优化6：独立的jvm中执行map/reduce&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;hive.exec.submitviachild&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;description/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Time taken:185.646 seconds
优化效果较为明显，该项配置修改在非本地模式的任务，在自己的jvm上提交任务，猜测减少了网络IO的时间啊，因此时间效率产生优化&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;优化7：压缩&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;property&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;hive.exec.compress.intermediate&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;property&amp;gt;&lt;/span&gt;
       &lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;hive.exec.compress.output&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;顺着优化6的思路，可以继续减少网络IO的时间，因此我们开启压缩，任务数据进行压缩会消耗部分cpu时间，但是发现结果中优化依然有效。
Time taken:167.921 seconds&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;优化8：基于上述配置开启本地模式&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Time taken:131.777 seconds
发现运行时间减少，但是多于仅开启本地模式的时间Time taken:64.826，发现多条优化策略在时间上慢于开启完全本地模式时间。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;在数据库调优课程上老师说过，调优无非是时间空间上的转化，牺牲空间换取时间或者牺牲时间效率进行空间优化，基于本次调优的一系列过程，我更深刻的学习到，hadoop本身特征是基于海量数据处理，可以将任务提交到多台处理的机制，那么也就决定了，hadoop生态框架利于扩展和海量数据的处理，那么也就牺牲了时间效率，因此，时间优化效果，本地模式更快的结论与此也相契合，如要注重时间效率可以采用spark等实时基于内存实时计算的大数据并行计算框架，可能基于时间的优化更为明显。&lt;/p&gt;</content><author><name>Yangtian</name></author><category term="blog" /><category term="hadoop" /><summary type="html">前言：数据库调优的PJ,我完成的实验部分内容包括基准测试，和一系列调优过程，因此在此记录一下调优过程，具体实验基准测试数据就不放在这里了，需要的请与我联系。本次测试基于hadoopHA环境基础上，使用mysql+hive对选取的数据集（TPC-H提供的GB级标准测试数据）进行性能测试，按照目前流行的TPC-H测试方法，进行了各种标准案例的测试，并在此基础上，进行了Hive配置参数，进行时间优化。 主要工具： CentOS release 6.4 hadoop-2.5.0 hive-1.2.2 TPC-H_on_Hive tpc-h 配置： 三台虚拟机，其中，两台2GB内存20GB硬盘，一台4GB内存20GB硬盘 tpc-h TPC-H基准测试包括22个查询（Q1~Q22）随机组成查询流 TPC-H模型的评价指标主要为各个查询的响应时间，即从提交查询到结果返回所需时间。TPC-H基准测试的度量单位：每小时执行的查询数（Qph@size）。其中H表示每小时系统执行复杂查询的平均次数，size表示数据库规模的大小，它能够反映出系统在处理查询时的能力。 TPC-H模型的表结构，8张表（表上有些约束等需要满足，参见TPC-H规范），如下： PART：表示零件的信息 SUPPLIER：表示供货商的信息 PARTSUPP：表示供货商的零件的信息 CUSTOMER：表示消费者的信息 ORDERS：表示订单的信息 LINEITEM：表示订单的信息 NATION：表示国家的信息 REGION：表示地区的信息</summary></entry><entry><title type="html">Python2代码转换到Python3工具</title><link href="https://yangtiancoder.github.io/blog/2019/01/11/python2to3/" rel="alternate" type="text/html" title="Python2代码转换到Python3工具" /><published>2019-01-11T00:00:00+00:00</published><updated>2019-01-11T00:00:00+00:00</updated><id>https://yangtiancoder.github.io/blog/2019/01/11/python2to3</id><content type="html" xml:base="https://yangtiancoder.github.io/blog/2019/01/11/python2to3/">&lt;p&gt;前言：果然懒惰是人开始使用工具的原动力，最近遇到Python2代码转到Python3代码的需求，懒得一处一处去修改，所以上网找有没有能使python2转化为python3的脚本，一查发现不仅有还是在官方文档中，使用之前怀疑可能不可靠或者会出现奇奇怪怪的bug之类的，使用了一下，真香，也太好用了，pj进度upup。&lt;/p&gt;

&lt;p&gt;现在记录一下使用方法，非常简单：&lt;/p&gt;

&lt;p&gt;1.首先找到你python安装的位置，就是下载python3安装环境，自带的运行指令也需要python环境，不过一般应该都装好了，然后再python\Tools\scripts文件中找到2to3.py文件如图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/2to3-1.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;2.使用Windows命令提示符（cmd）打开2to3.py脚本所在位置，至于怎么切换cmd操作目录。。。这种应该大家都会的吧&lt;/p&gt;

&lt;p&gt;3.在脚本下运行命令指定需要转变版本的python文件，注意-w后面的是需要转换版本文件的位置，我的这个文件在桌面的一个目录下，如图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/2to3-3.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;4.大功告成，转换文件会被直接覆盖，然后生成.bak文件是你之前版本也就是python2的代码&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/2to3-4.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;完毕，很简单吧，我要继续去写bug去了。。。&lt;/p&gt;

&lt;p&gt;突然意识到这是2019年第一篇博客。。。&lt;/p&gt;</content><author><name>Yangtian</name></author><category term="blog" /><category term="python" /><summary type="html">前言：果然懒惰是人开始使用工具的原动力，最近遇到Python2代码转到Python3代码的需求，懒得一处一处去修改，所以上网找有没有能使python2转化为python3的脚本，一查发现不仅有还是在官方文档中，使用之前怀疑可能不可靠或者会出现奇奇怪怪的bug之类的，使用了一下，真香，也太好用了，pj进度upup。</summary></entry><entry><title type="html">hadoop原理(二)（Hadoop2.x版本）</title><link href="https://yangtiancoder.github.io/blog/2018/12/02/hadoop-second/" rel="alternate" type="text/html" title="hadoop原理(二)（Hadoop2.x版本）" /><published>2018-12-02T00:00:00+00:00</published><updated>2018-12-02T00:00:00+00:00</updated><id>https://yangtiancoder.github.io/blog/2018/12/02/hadoop-second</id><content type="html" xml:base="https://yangtiancoder.github.io/blog/2018/12/02/hadoop-second/">&lt;h1 id=&quot;hdfs的主要设计理念&quot;&gt;HDFS的主要设计理念&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;1、存储超大文件&lt;br /&gt;
这里的“超大文件”是指几百MB、GB甚至TB级别的文件。&lt;/li&gt;
  &lt;li&gt;2、最高效的访问模式是 一次写入、多次读取(流式数据访问)&lt;br /&gt;
HDFS存储的数据集作为hadoop的分析对象。在数据集生成后，长时间在此数据集上进行各种分析。每次分析都将设计该数据集的大部分数据甚至全部数据，因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要。&lt;/li&gt;
  &lt;li&gt;3、运行在普通廉价的服务器上&lt;br /&gt;
HDFS设计理念之一就是让它能运行在普通的硬件之上，即便硬件出现故障，也可以通过容错策略来保证数据的高可用。&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;hdfs的忌讳&quot;&gt;HDFS的忌讳&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;1、将HDFS用于对数据访问要求低延迟的场景 &lt;br /&gt;
由于HDFS是为高数据吞吐量应用而设计的，必然以高延迟为代价。&lt;/li&gt;
  &lt;li&gt;2、存储大量小文件  &lt;br /&gt;
HDFS中元数据（文件的基本信息）存储在namenode的内存中，而namenode为单点，小文件数量大到一定程度，namenode内存就吃不消了。&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;hdfs中如何存储nndn及snn主要功能&quot;&gt;HDFS中如何存储（NN、DN及SNN主要功能）&lt;/h1&gt;

&lt;p&gt;整体架构如图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/HDFS-1.png?raw=true&quot; alt=&quot;HDFS-1.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;client&quot;&gt;Client&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;文件切分。文件上传 HDFS 的时候，Client 将文件切分成 一个一个的Block，然后进行存储。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;与 NameNode 交互，获取文件的位置信息;与 DataNode 交互，读取或者写入数据。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Client提供一些命令来管理 HDFS，比如启动或者关闭HDFS。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;namenode功能&quot;&gt;NameNode功能&lt;/h2&gt;

&lt;p&gt;NN是HDFS主从结构中主节点上运行的主要进程，它负责管理从节点DN。NN维护着整个文件系统的文件目录树，文件目录的元信息和文件的数据块索引。这些信息以两种信息保存在本文文件系统中，一种是文件系统镜像（文件名字fsimage），另一种是fsimage的编辑日志（文件名字edits）。  fsimage中保存着某一特定时刻HDFS的目录树、元信息和文件数据块的索引等信息，后续的对这些信息的改动，则保存在编辑日志中，它们一起提供了一个完整的NN的第一关系。通过NN，Client还可以了解到数据块所在的DN的信息。需要注意的是，NN中关于DN的信息是不会保存到NN的本地文件系统的，也就是上面提到的fsimage和edits中。NN每次启动时，都会通过每个DN的上报来动态的建立这些信息。这些信息也就构成了NN第二关系。&lt;/p&gt;

&lt;p&gt;综上：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;1、NameNode主要功能：协调客户端对文件的访问&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;2、NameNode保存metadata（元数据，除了文件内容之外的都是元数据）信息包括：&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;1）文件owership和permissions；文件包含哪些块&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;2）Block保存在哪个DataNode（由DataNode启动时上报）&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;3、NameNode的metadate信息在启动后加载到内存：&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;1）metadata存储到磁盘文件名为“fsimages”（命名空间镜像文件，NN主要根据fsimage来进行数据操作）&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;2）edits记录对metadata的操作日志&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;datanode功能&quot;&gt;DataNode功能&lt;/h2&gt;

&lt;p&gt;DN是HDFS中硬盘IO最忙碌的部分：将HDFS的数据块写到Linux本地文件系统中，或者从这些数据块中读取数据。DN作为从节点，会不断的向NN发送心跳。初始化时，每个DN将当前节点的数据块上报给NN。NN也会接收来自NN的指令，比如创建、移动或者删除本地的数据块，并且将本地的更新上报给NN。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;1、在NameNode的统一调度下进行数据块的创建、存储、删除和复制&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;2、启动DN线程的时候会向NN汇报block信息,之后NN根据汇报的block信息来找到block数据&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;3、通过向NN发送心跳保持与其联系（3秒一次）&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;secondarynamenode功能&quot;&gt;SecondaryNameNode功能&lt;/h2&gt;

&lt;p&gt;首先是NN中的Fsimage和edits文件通过网络拷贝，到达SNN服务器中，拷贝的同时，用户的实时在操作数据，那么NN中就会从新生成一个edits来记录用户的操作，而另一边的SＮＮ将拷贝过来的edits和fsimage进行合并，合并之后就替换NN中的fsimage。之后NN根据fsimage进行操作（当然每隔一段时间就进行替换合并，循环）。当然新的edits与合并之后传输过来的fsimage会在下一次时间内又进行合并。&lt;/p&gt;

&lt;p&gt;需要注意的是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;1、它不是NN的备份（但可以做备份），它主要工作是帮助NN合并editslog，减少NN启动时间。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;2、SNN执行合并时机：根据配置文件设置的时间间隔fs.checkpoint.period默认3600秒；根据配置文件设置edits log大小fs.checkpoint.size规定edit文件的最大值默认64MB。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;3、当删除一个文件的时候其实并不是马上删除，而是在edits log中记录，到一定时间与fsimage通过SNN进行合并的时候进行删除。由于涉及大多的IO和消耗CPU，所以在NN中不做数据操作的合并，而是让另一个机器的CPU去计算实现SNN根据时间来不断合并各个NN，这样用户体验感比较好，速度也是比较快。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;4、那么通过SNN合并之后的新的FSimage和edits log会被推送到NN中并且替换原来的FSimage和edits log，这样NN 里面隔段时间就是新的数据。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;hdfs的读取和写入&quot;&gt;HDFS的读取和写入&lt;/h1&gt;

&lt;p&gt;如图：
&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/HDFS-2.png?raw=true&quot; alt=&quot;HDFS-2.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;HDFS的文件读取原理，主要包括以下几个步骤：&lt;/p&gt;

&lt;p&gt;（1）首先调用FileSystem对象的open方法，事实上是一个DistributedFileSystem的实例。&lt;/p&gt;

&lt;p&gt;（2）分布式文件系统通过使用RPC（远程过程调用）来调用namenode，确定文件起始块的位置。&lt;/p&gt;

&lt;p&gt;（3）分布式文件系统的DistributedFileSystem类返回一个支持文件定位的输入流FSDataInputStream对象，FSDataInputStream对象接着封装DFSInputStream对象（存储着文件起始几个块的datanode地址），客户端对这个输入流调用read()方法。&lt;/p&gt;

&lt;p&gt;（4）DFSInputStream连接距离最近的datanode，通过反复调用read方法，将数据从datanode传输到客户端。&lt;/p&gt;

&lt;p&gt;（5）到达块的末端时，DFSInputStream关闭与该datanode的连接，寻找下一个块的最佳datanode。&lt;/p&gt;

&lt;p&gt;（6）客户端完成读取，对FSDataInputStream调用close()方法关闭连接。&lt;/p&gt;

&lt;p&gt;HDFS的文件写入原理，主要包括以下几个步骤：&lt;/p&gt;

&lt;p&gt;如图：
&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/HDFS-3.png?raw=true&quot; alt=&quot;HDFS-3.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;（1）客户端通过对DistributedFileSystem对象调用create()函数来新建文件。&lt;/p&gt;

&lt;p&gt;（2）分布式文件系统对namenod创建一个RPC调用，在文件系统的命名空间中新建一个文件。&lt;/p&gt;

&lt;p&gt;（3）Namenode对新建文件进行检查无误后，分布式文件系统返回给客户端一个FSDataOutputStream对象，FSDataOutputStream对象封装一个DFSoutPutstream对象，负责处理namenode和datanode之间的通信，客户端开始写入数据。&lt;/p&gt;

&lt;p&gt;（4）FSDataOutputStream将数据分成一个一个的数据包，写入内部队列“数据队列”，DataStreamer负责将数据包依次流式传输到由一组namenode构成的管线中。&lt;/p&gt;

&lt;p&gt;（5）DFSOutputStream维护着确认队列来等待datanode收到确认回执，收到管道中所有datanode确认后，数据包从确认队列删除。&lt;/p&gt;

&lt;p&gt;（6）客户端完成数据的写入，对数据流调用close()方法。&lt;/p&gt;

&lt;p&gt;（7）namenode确认完成。&lt;/p&gt;</content><author><name>Yangtian</name></author><category term="blog" /><category term="hadoop" /><summary type="html">HDFS的主要设计理念</summary></entry><entry><title type="html">hadoop原理(一)（Hadoop2.x版本）</title><link href="https://yangtiancoder.github.io/blog/2018/11/27/hadoop-first/" rel="alternate" type="text/html" title="hadoop原理(一)（Hadoop2.x版本）" /><published>2018-11-27T00:00:00+00:00</published><updated>2018-11-27T00:00:00+00:00</updated><id>https://yangtiancoder.github.io/blog/2018/11/27/hadoop-first</id><content type="html" xml:base="https://yangtiancoder.github.io/blog/2018/11/27/hadoop-first/">&lt;h1 id=&quot;hadoop&quot;&gt;Hadoop&lt;/h1&gt;

&lt;p&gt;Hadoop是一个用于海量数据统计分析的分布式计算框架，封装了分布式计算中比较困难的进程间通信、负载均衡，任务调度等模块，简单来说，hadoop 就是一个大数据解决方案。它提供了一套分布式系统基础架构， 核心内容包含hdfs和mapreduce。hadoop2.0以后引入yarn。&lt;/p&gt;

&lt;p&gt;如图：
&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/mapReduce-0.png?raw=true&quot; alt=&quot;hadoop1&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;主要模块&quot;&gt;主要模块&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Hadoop Distributed File System（HDFS）：分布式文件存储系统。&lt;/li&gt;
  &lt;li&gt;MapReduce：并行计算框架（可以自定义计算逻辑的部分）&lt;/li&gt;
  &lt;li&gt;Yet Another Resource Negotiator（YARN）：另一种资源协调者（顾名思义，Hadoop1.x采用的不是这一个资源管理器）&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;mapreduce和yarn&quot;&gt;MapReduce和YARN&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;mapreduce是一个分布式运算程序的编程框架,是hadoop数据分析的核心.&lt;/li&gt;
  &lt;li&gt;mapreduce的核心思想是将用户编写的逻辑代码和架构中的各个组件整合成一个分布式运算程序,实现一定程序的并行处理海量数据,提高效率.&lt;/li&gt;
  &lt;li&gt;海量数据难以在单机上处理,而一旦将单机版程序扩展到集群上进行分布式运行势必将大大增加程序的复杂程度.引入mapreduce架构,开发人员可以将精力集中于数据处理的核心业务逻辑上,而将分布式程序中的公共功能封装成框架,以降低开发的难度.&lt;/li&gt;
  &lt;li&gt;一个完整的mapreduce程序有三类实例进程
    &lt;ul&gt;
      &lt;li&gt;MRAppMaster:负责整个程序的协调过程&lt;/li&gt;
      &lt;li&gt;MapTask:负责map阶段的数据处理&lt;/li&gt;
      &lt;li&gt;ReduceTask:负责reduce阶段的数据处理&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;hadoop1x中的mapreduce&quot;&gt;Hadoop1.x中的MapReduce&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/mapReduce-1.png?raw=true&quot; alt=&quot;mapReduce-1.png&quot; /&gt;
MapReduce在hadoop1.x作业执行的流程图&lt;/p&gt;

&lt;p&gt;在hadoop1.x中，首先客户端要编写好mapreduce程序，然后提交作业也就是job，job的信息会发送到JobTracker上，并为该job分配一个ID值，接下来做检查操作，确认输入目录是否存在，如果不存在，则会抛错，如果存在继续检查输出目录是否存在，如果存在则会抛错，否则继续运行；当检查工作都做好了JobTracker就会配置Job需要的资源了。&lt;/p&gt;

&lt;p&gt;其中，&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;JobTracker: 主要负责资源监控管理和作业调度 
（1）监控所有TaskTracker 与job的健康状况,一旦发现失败,就将相应的任务转移到其他节点； 
（2）同时JobTracker会跟踪任务的执行进度、资源使用量等信息,并将这些信息告诉任务调度器,而调度器会在资源出现空闲时,选择合适的任务使用这些资源。&lt;/li&gt;
  &lt;li&gt;TaskTracker：是JobTracker与Task之前的桥梁 &lt;br /&gt;
（1）首先用户程序 （JobClient） 提交了一个 job，job 的信息会发送到 Job Tracker 中，Job Tracker 是 Map-reduce 框架的中心，他需要与集群中的机器定时通信 (heartbeat), 需要管理哪些程序应该跑在哪些机器上，需要管理所有 job 失败、重启等操作。&lt;br /&gt;
（2）TaskTracker 是 Map-reduce 集群中每台机器都有的一个部分，他做的事情主要是监视自己所在机器的资源情况。&lt;br /&gt;
（3）TaskTracker 同时监视当前机器的 tasks 运行状况。TaskTracker 需要把这些信息通过 heartbeat发送给JobTracker，JobTracker 会搜集这些信息以给新提交的 job 分配运行在哪些机器上。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;hadoop1x的mapreduce框架的主要局限&quot;&gt;Hadoop1.x的MapReduce框架的主要局限：&lt;/h3&gt;
&lt;p&gt;（1）JobTracker 是 Map-reduce 的集中处理点，存在单点故障，可靠性差； &lt;br /&gt;
（2）JobTracker 完成了太多的任务，造成了过多的资源消耗，当 map-reduce job 非常多的时候，会造成很大的内存开销，潜在来说，也增加了 JobTracker 失效的风险。&lt;/p&gt;

&lt;h2 id=&quot;hadoop2x中的mapreduce&quot;&gt;Hadoop2.x中的MapReduce&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/mapReduce-2.png?raw=true&quot; alt=&quot;mapReduce-2.png&quot; /&gt;
MapReduce在hadoop2.x作业执行的流程图&lt;/p&gt;

&lt;p&gt;两个框架最大的区别在于原来框架中的JobTracker和TaskTracker不见了，取而代之的是ResourceManager、NodeManager和Application Master三个。重构根本的思想是将 JobTracker 两个主要的功能分离成单独的组件，这两个功能是资源管理和任务调度 / 监控。Resource Manager负责全局资源分配。&lt;/p&gt;

&lt;p&gt;YARN总体上仍然是master/slave结构，在整个资源管理框架中，resourcemanager为master，nodemanager是slave。Resourcemanager负责对各个nademanger上资源进行统一管理和调度。当用户提交一个应用程序时，需要提供一个用以跟踪和管理这个程序的ApplicationMaster，它负责向ResourceManager申请资源，并要求NodeManger启动可以占用一定资源的任务。&lt;/p&gt;

&lt;p&gt;ResourceManager是Master上一个独立运行的进程，负责集群统一的资源管理、调度、分配等等；NodeManager是Slave上一个独立运行的进程，负责上报节点的状态；App Master和Container是运行在Slave上的组件，Container是yarn中分配资源的一个单位，包涵内存、CPU等等资源，yarn以Container为单位分配资源。&lt;/p&gt;

&lt;p&gt;Client向ResourceManager提交的每一个应用程序都必须有一个Application Master，它经过ResourceManager分配资源后，运行于某一个Slave节点的Container中，具体做事情的Task，同样也运行与某一个Slave节点的Container中。RM，NM，AM乃至普通的Container之间的通信，都是用RPC机制。&lt;/p&gt;

&lt;h3 id=&quot;hadoop2x中的mapreduce优点&quot;&gt;Hadoop2.x中的MapReduce优点&lt;/h3&gt;

&lt;p&gt;（1）新框架将JobTracker的分离，减少了它的资源消耗，使系统更容易从单点故障中恢复，并且监测每个作业子任务状态的程序分布式化了，更安全。 &lt;br /&gt;
（2）在新框架中，ApplicationMaster是可变的，可以为不同的计算框架编写自己的Application Master，使得更多的计算框架可以运行在Hadoop集群上。 &lt;br /&gt;
（3）老的框架中，JobTracker一个很大的负担就是监控job下的tasks的运行状况，现在，这个部分就扔给ApplicationMaster做了，而ResourceManager中有一个模块叫ApplicationsManager，它是监测ApplicationMaster的运行状况，如果出问题，会将其在其他机器上重启。 &lt;br /&gt;
（4）Container很好地起到了资源隔离的作用，让资源更好地被利用起来。&lt;/p&gt;</content><author><name>Yangtian</name></author><category term="blog" /><category term="hadoop" /><summary type="html">Hadoop</summary></entry><entry><title type="html">数据库中的事务和锁总结</title><link href="https://yangtiancoder.github.io/blog/2018/11/22/Mysql-lock/" rel="alternate" type="text/html" title="数据库中的事务和锁总结" /><published>2018-11-22T00:00:00+00:00</published><updated>2018-11-22T00:00:00+00:00</updated><id>https://yangtiancoder.github.io/blog/2018/11/22/Mysql-lock</id><content type="html" xml:base="https://yangtiancoder.github.io/blog/2018/11/22/Mysql-lock/">&lt;h1 id=&quot;数据库事务&quot;&gt;数据库事务&lt;/h1&gt;
&lt;p&gt;简单来说，事务就是满足ACID特性的一系列操作。我们可以用commit提交一个事务，也可以用rollback回滚事务。&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;事务开启&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;这里可以是一系列增删改查&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COMMIT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ROLLBACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;acid&quot;&gt;ACID&lt;/h2&gt;
&lt;h3 id=&quot;atomicity原子性&quot;&gt;Atomicity原子性&lt;/h3&gt;
&lt;p&gt;即事务要么全部成功要么全部失败，中间有步骤失败就应该回滚。&lt;/p&gt;

&lt;p&gt;例如A账户给B账户转账&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transaction&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;money&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;money&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'A'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;money&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;money&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'B'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;rollback&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;commit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;如果没有原子性的话，A扣了100块钱后，突然断电了，那么B就没有加100，A却少了100。很明显这是不对的。&lt;/p&gt;

&lt;h3 id=&quot;consistency一致性&quot;&gt;Consistency一致性&lt;/h3&gt;
&lt;p&gt;数据库在事务执行前后都保持一致性状态。&lt;/p&gt;

&lt;p&gt;在一致性状态下，所有事务对一个数据的读取结果都是相同的。&lt;/p&gt;

&lt;p&gt;对银行转帐事务，假设在事务开始之前，A和B的账户总额为2000，那么不管事务成功还是失败，应该保证事务结束后ACCOUNT表中A和B的存款总额依旧为2000。&lt;/p&gt;

&lt;h3 id=&quot;isolation隔离性&quot;&gt;Isolation隔离性&lt;/h3&gt;
&lt;p&gt;一个事务所做的修改在最终提交以前，对其它事务是不可见的。&lt;/p&gt;

&lt;p&gt;例如两个事务都在对同个数据进行操作，如果没有相互隔离，那么将引起数据的不一致，进而可能导致错误。&lt;/p&gt;

&lt;p&gt;举个栗子，假设有如下一张表:&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;mysql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InnoDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;目前该表有一个字段，而且有一条数据，该字段值为1。接下来有两个事务，都对该表做如下操作&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;事务A&lt;/th&gt;
      &lt;th&gt;事务B&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;启动事务，查询得到值1&lt;/td&gt;
      &lt;td&gt;启动事务&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;-&lt;/td&gt;
      &lt;td&gt;查询得到值1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;-&lt;/td&gt;
      &lt;td&gt;将值1改为2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;查询得到值V1&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;-&lt;/td&gt;
      &lt;td&gt;提交事务&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;查询得到值V2&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;提交事务&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;查询得到值V3&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;我们通过不同的隔离级别来分析V1、V2和V3的值&lt;/p&gt;

&lt;h4 id=&quot;未提交读read-uncommitted&quot;&gt;未提交读（read uncommitted）&lt;/h4&gt;
&lt;p&gt;在这种级别下V1、V2、V3都是2，可以看到的是事务B在为提交时，V1查询到的结果已经是被事务B修改后的值。&lt;/p&gt;

&lt;p&gt;也就是说在这种级别下，事务中的修改，即使没有提交，对其它事务也是可见的&lt;/p&gt;

&lt;p&gt;这种隔离级别基本不会被用到。&lt;/p&gt;

&lt;h4 id=&quot;提交读read-committed&quot;&gt;提交读（read committed）&lt;/h4&gt;
&lt;p&gt;在这种级别下，V1是1，V2和V3是2，可见其他事务在没有提交前，所做的修改对其他事务都是不可见的，一个事务只能读取已经提交的事务所做的修改。&lt;/p&gt;

&lt;p&gt;这种级别，我们每次读取的时候就能够读取已经提交的数据。&lt;/p&gt;

&lt;h4 id=&quot;可重复读repeatable-read&quot;&gt;可重复读（repeatable read）&lt;/h4&gt;
&lt;p&gt;在这种级别下V1、V2都是1，V3是2，可见事务开始后整个过程的数据都是一致的，前后读到的数据都不会变。&lt;/p&gt;

&lt;p&gt;很多时候，我们希望在事务里面，只要我们没有修改数据，那么数据就不应该有变化，无论我们读取多少次，这就是可重复读，保证在同一个事务中多次读取同样数据的结果是一样的，这也就是可重复读的作用。&lt;/p&gt;

&lt;h4 id=&quot;可串行化serializable&quot;&gt;可串行化（serializable）&lt;/h4&gt;
&lt;p&gt;在这种级别下，事务B在执行到修改val值为2时会被锁住，必须等到事务A提交后才可以继续执行。此时V1和V2都是1，V3是2。&lt;/p&gt;

&lt;p&gt;如果在执行事务的时候，有其他事务正在执行，那么它将等待其他事务执行结束之后才能被执行。相当于给数据加了锁。&lt;/p&gt;

&lt;h4 id=&quot;隔离性如何实现的&quot;&gt;隔离性如何实现的&lt;/h4&gt;
&lt;p&gt;数据库会根据不同隔离级别判断如何创建和使用视图，通过视图来获取数据&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;隔离级别&lt;/th&gt;
      &lt;th&gt;视图创建时机&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;可重复读&lt;/td&gt;
      &lt;td&gt;视图在事务启动时创建，整个事务期间都使用该视图&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;提交读&lt;/td&gt;
      &lt;td&gt;视图在sql语句执行的时候创建&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;未提交读&lt;/td&gt;
      &lt;td&gt;不使用视图&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;串行化&lt;/td&gt;
      &lt;td&gt;不使用视图，采用加锁的方式&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;在mysql中，每条记录更新的时候都会同时记录一条回滚操作，通过这个回滚操作，都可以得到前一个状态的值&lt;/p&gt;

&lt;p&gt;例如在一个事务里，一个字段从1改成2，再从2改成3，最终从3改成4，那么便会产生3个回滚操作：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-----------------------------------------
| （回滚段）                               |
|将2回滚为1 &amp;lt;--- 将3回滚成2 &amp;lt;---- 将4回滚成3 |  &amp;lt;----- 当前值4
-----------------------------------------
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由此产生了数据的不同版本，这称之为MVCC（多版本并发控制）&lt;/p&gt;

&lt;p&gt;具体的可以看这几篇文章： &lt;br /&gt;
&lt;a href=&quot;https://www.jianshu.com/p/d75fcdeb07a3&quot;&gt;数据库事务特征、数据库隔离级别，以及各级别数据库加锁情况(含实操)–read uncommitted篇&lt;/a&gt;  &lt;br /&gt;
&lt;a href=&quot;https://www.imooc.com/article/17290&quot;&gt;MySQL数据库事务各隔离级别加锁情况–read committed &amp;amp;&amp;amp; MVCC&lt;/a&gt;  &lt;br /&gt;
&lt;a href=&quot;https://www.imooc.com/article/17289&quot;&gt;MySQL数据库事务各隔离级别加锁情况–Repeatable Read &amp;amp;&amp;amp; MVCC&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;锁&quot;&gt;锁&lt;/h3&gt;
&lt;h3 id=&quot;排他锁x锁悲观锁&quot;&gt;排他锁（X锁，悲观锁）&lt;/h3&gt;
&lt;p&gt;很多时候我们并不需要那么高的隔离级别，我们通过一种叫排他锁的也能够实现可串行化。&lt;/p&gt;

&lt;p&gt;例如：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select * from goods where id = 1 for update;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;排他锁的申请前提：没有线程对该结果集中的任何行数据使用排他锁或共享锁，否则申请会阻塞。&lt;/p&gt;

&lt;p&gt;for update仅适用于InnoDB，且必须在事务块(BEGIN/COMMIT)中才能生效。在进行事务操作时，通过“for update”语句，MySQL会对查询结果集中每行数据都添加排他锁，其他线程对该记录的更新与删除操作都会阻塞。排他锁包含行锁、表锁。&lt;/p&gt;

&lt;p&gt;假设有A、B两个用户同时各购买一件 id=1 的商品，用户A获取到的库存量为 1000，用户B获取到的库存量也为 1000，用户A完成购买后修改该商品的库存量为 999，用户B完成购买后修改该商品的库存量为 999，此时库存量数据产生了不一致。&lt;/p&gt;

&lt;p&gt;此时我们可以采用排他锁&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;每次获取商品时，对该商品加排他锁。也就是在用户A获取获取 id=1 的商品信息时对该行记录加锁，期间其他用户阻塞等待访问该记录。悲观锁适合写入频繁的场景。&lt;/p&gt;

&lt;h4 id=&quot;乐观锁&quot;&gt;乐观锁&lt;/h4&gt;
&lt;p&gt;加锁会对数据库有一定的性能损耗，因此我们还可以通过一种叫乐观锁的机制来实现上述过程&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;先读取产品数量&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;接下来开启事务&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;注意这里的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;and account = currCount&lt;/code&gt;，通过程序中获取到的产品数量与数据库中的产品量相等才执行更新。&lt;/p&gt;

&lt;p&gt;每次获取商品时，不对该商品加锁。在更新数据的时候需要比较程序中的库存量与数据库中的库存量是否相等，如果相等则进行更新，反之程序重新获取库存量，再次进行比较，直到两个库存量的数值相等才进行数据更新。乐观锁适合读取频繁的场景。&lt;/p&gt;

&lt;p&gt;当然我们也可以给表加一个版本字段，每次更新行的数据时，版本就加1。然后更新的时候判断版本也是可以的。&lt;/p&gt;

&lt;h3 id=&quot;durability持久性&quot;&gt;Durability持久性&lt;/h3&gt;
&lt;p&gt;一旦事务提交，则其所做的修改将会永远保存到数据库中。即使系统发生崩溃，事务执行的结果也不能丢失。&lt;/p&gt;

&lt;p&gt;可以通过数据库备份和恢复来实现，在系统发生奔溃时，使用备份的数据库进行数据恢复。&lt;/p&gt;</content><author><name>Yangtian</name></author><category term="blog" /><category term="MySQL" /><summary type="html">数据库事务 简单来说，事务就是满足ACID特性的一系列操作。我们可以用commit提交一个事务，也可以用rollback回滚事务。 - 事务开启 BEGIN - 这里可以是一系列增删改查 COMMIT; - ROLLBACK;</summary></entry><entry><title type="html">却道天凉好个秋。</title><link href="https://yangtiancoder.github.io/life/2018/11/15/TY-qdtlhgq/" rel="alternate" type="text/html" title="却道天凉好个秋。" /><published>2018-11-15T00:00:00+00:00</published><updated>2018-11-15T00:00:00+00:00</updated><id>https://yangtiancoder.github.io/life/2018/11/15/TY-qdtlhgq</id><content type="html" xml:base="https://yangtiancoder.github.io/life/2018/11/15/TY-qdtlhgq/">&lt;p&gt;长到这么大，说不出来我最爱的一首歌，说不出来我最爱的一个人。时常觉得人生其实没那么有趣，偶尔也会质疑活着的意义，所有来自于书上和别人口中的意义都不曾说服过我。但今天突然觉得，大概人生最大的意义就是用余生去找到那些最爱吧。                                                                ——来自XXX&lt;/p&gt;

&lt;p&gt;照例先放一段看起来很有文化水平的话，不知道是谁说得，只是觉得说的很对。。。&lt;/p&gt;

&lt;p&gt;这是来上海的第一篇随笔了吧，想起上一次还是在家马上离开时候写的这个，看了一下大概是8月下旬&lt;a href=&quot;https://yangtiancoder.github.io/life/2018/08/23/The-end-of-the-holiday/&quot;&gt;上一篇戳这里：假期结束&lt;/a&gt;呕心沥血的应付之作。。。转眼又过去快三个月了，我现在还能想起来临走前几天还在床上兢兢业业的写，毕竟是作为一个假期什么也没干的总结。。。只能说来学校之后变得很懒，几次想写点东西，学校怎么怎么样呀，同学怎么怎么样呀，矫情一下，想一想还是算了，可能是上了年纪的原因，肥宅属性越来越明显，得过且过，有想说的东西，想想好像和别人没什么关系，算了那就不说了。。。真方便，哈哈哈&lt;/p&gt;

&lt;p&gt;真香警告一次，言归正传，又是一个不想学习的晚上（PS：大概是第10086个不想学习的晚上），决定这次写一下，这次突然想写的契机是什么呢。&lt;/p&gt;

&lt;h1 id=&quot;契机&quot;&gt;契机&lt;/h1&gt;

&lt;p&gt;来自11月13日下午的微信，突然被拉进了小学的同学群，上图：
&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/xiaoxue-1.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;顿时心里是蒙蔽的，看到了里面许多熟悉的人，内心只能用我靠来形容，好多熟人，后来知道大概是刚好10年大家准备过年回去聚会，然后就开始拉人，没想到就大家互相拉群最后也有40人了，我当时的感觉是满感慨的，其实有好多我认为关系比较好的同学，毕业之后就没联系过了，反正就感觉好有回忆感，都是熟悉的人名,我还记得你们的光荣事迹哦。。。我们县城地方比较小，一部分人也上一个初中，另一个初中离我们也不远，但好像几乎也没见过（我还没有QQ微信），后来有一些又变成了高中同班的小学同学（虽然看起来很乱但是实际也是这么乱），其他基本都有10年没有见过了或者7年没见过，但是我还能记得许多人，印象中他们是怎么怎么样的。&lt;/p&gt;

&lt;h1 id=&quot;回忆&quot;&gt;回忆&lt;/h1&gt;

&lt;p&gt;这一周是有点丧的，就是莫名的什么也不想干。不过我觉得这件小事也给我充了一下电，反正就是蛮开心的，嘻嘻。&lt;/p&gt;

&lt;p&gt;神图预警：
&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/xiaoxue-2.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我发现了这张照片，第一次见到，原来小学还有合影呢，看到好多熟悉的样子，找到了我自己，小时候帅的一批，开心。&lt;/p&gt;

&lt;p&gt;然后说点什么呢。。。开始尬聊，我想起我上小学的时候，我的外号是（马大哈），一般老师就这么叫我，我大概是是最马虎的一个了吧，哈哈哈，想起来真有意思，我小时候最热衷的就是——玩，先写完作业的人可以出去玩，所以我写作业很能对付，间接导致了我字写的很丑。。。哈哈哈&lt;/p&gt;

&lt;p&gt;我又想起了很多事，包括小学门口被抢劫，调戏女同学等等。。。哈哈哈，这种事简直是家常便饭，不得不说我小时候厚脸皮还是可以的，记得小学的时候大概总有同学没完成作业，然后借口说忘带了，老师很有办法，那就是回家去拿，回家需要人监督，我就是经常陪人回家去拿作业的那个人，现在想起来总结一下我小时候就是一个字——欠儿。
来自官方的东北话注释：&lt;br /&gt;
&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/xiaoxue-3.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;嗯，怎么说呢，想起自己小时候也是蛮有意思的，大家都变化很大啊，偷偷点进去熟悉的人的朋友圈，有的人能看见一些动态，好像认识又好像不认识的感觉，这种感觉很奇怪，嗯，怎么说呢，无一例外大家都长大了，或多或少都有一些变化了，时间过的真快。&lt;/p&gt;

&lt;h1 id=&quot;总结&quot;&gt;总结&lt;/h1&gt;

&lt;p&gt;这种事写个总结是很奇怪的，我也没什么办法，反正就是想到什么就写什么，可能我觉得三个标题比较好看。&lt;/p&gt;

&lt;p&gt;上图：
&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/xiaoxue-4.png?raw=true&quot; alt=&quot;&quot; /&gt;
我有一个习惯，我觉得有纪念意义的东西习惯带在身边，可能也不是，反正我觉得看到这些东西比较心安。&lt;/p&gt;

&lt;p&gt;跑题了，拉到群里当天晚上回去又翻了一遍，虽然一直带着但是其实也没怎么看，我小时候的理想是当一名伟人。。。哈哈哈，果然我从小就这么敷衍了，难怪现在改不了。&lt;/p&gt;

&lt;p&gt;还有一些同学写着要当服装设计师，游戏设计师（活捉一名同行），现在看来果然大家都是随便写的，小时候的梦想都不算梦想吧，离我们现在太遥远了，就像我们现在离未来还是很遥远一样，一生那么长，要想不负时光，不负自己真的很难，我也时常幻想以后在一个岗位上，日复一日的工作状态会是怎样的呢，再过10年之后的我会是怎么样的呢，我还会继续写博客么，再翻到10年前写博客的时候会不会想起现在，我穿着卫衣带着帽子裹得严严实实边听歌边写博客的时刻，感觉真奇妙哈。&lt;/p&gt;

&lt;p&gt;最后，总结一下，断断续续写了三天，还是保持我一贯十分没有文采想到哪写到哪的风格，写完之后通读了一遍我真怀疑自己是不是喝多了的时候写的，写完博客自己看一遍，开心！&lt;/p&gt;</content><author><name>Yangtian</name></author><category term="life" /><category term="随笔" /><summary type="html">长到这么大，说不出来我最爱的一首歌，说不出来我最爱的一个人。时常觉得人生其实没那么有趣，偶尔也会质疑活着的意义，所有来自于书上和别人口中的意义都不曾说服过我。但今天突然觉得，大概人生最大的意义就是用余生去找到那些最爱吧。 ——来自XXX</summary></entry><entry><title type="html">select、epoll区别详解（转）</title><link href="https://yangtiancoder.github.io/blog/2018/11/10/java-IO-selectepoll/" rel="alternate" type="text/html" title="select、epoll区别详解（转）" /><published>2018-11-10T00:00:00+00:00</published><updated>2018-11-10T00:00:00+00:00</updated><id>https://yangtiancoder.github.io/blog/2018/11/10/java-IO-selectepoll</id><content type="html" xml:base="https://yangtiancoder.github.io/blog/2018/11/10/java-IO-selectepoll/">&lt;h1 id=&quot;select实现&quot;&gt;select实现&lt;/h1&gt;

&lt;h2 id=&quot;select的调用过程如下所示&quot;&gt;select的调用过程如下所示：&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/java-io-selectepoll1.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;（1）使用copy_from_user从用户空间拷贝fd_set到内核空间&lt;/p&gt;

&lt;p&gt;（2）注册回调函数__pollwait&lt;/p&gt;

&lt;p&gt;（3）遍历所有fd，调用其对应的poll方法（对于socket，这个poll方法是sock_poll，sock_poll根据情况会调用到tcp_poll,udp_poll或者datagram_poll）&lt;/p&gt;

&lt;p&gt;（4）以tcp_poll为例，其核心实现就是__pollwait，也就是上面注册的回调函数。&lt;/p&gt;

&lt;p&gt;（5）__pollwait的主要工作就是把current（当前进程）挂到设备的等待队列中，不同的设备有不同的等待队列，对于tcp_poll来说，其等待队列是sk-&amp;gt;sk_sleep（注意把进程挂到等待队列中并不代表进程已经睡眠了）。在设备收到一条消息（网络设备）或填写完文件数据（磁盘设备）后，会唤醒设备等待队列上睡眠的进程，这时current便被唤醒了。&lt;/p&gt;

&lt;p&gt;（6）poll方法返回时会返回一个描述读写操作是否就绪的mask掩码，根据这个mask掩码给fd_set赋值。&lt;/p&gt;

&lt;p&gt;（7）如果遍历完所有的fd，还没有返回一个可读写的mask掩码，则会调用schedule_timeout是调用select的进程（也就是current）进入睡眠。当设备驱动发生自身资源可读写后，会唤醒其等待队列上睡眠的进程。如果超过一定的超时时间（schedule_timeout指定），还是没人唤醒，则调用select的进程会重新被唤醒获得CPU，进而重新遍历fd，判断有没有就绪的fd。&lt;/p&gt;

&lt;p&gt;（8）把fd_set从内核空间拷贝到用户空间。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结：&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;select的几大缺点:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;（1）每次调用select，都需要把fd集合从用户态拷贝到内核态，这个开销在fd很多时会很大&lt;/p&gt;

&lt;p&gt;（2）同时每次调用select都需要在内核遍历传递进来的所有fd，这个开销在fd很多时也很大&lt;/p&gt;

&lt;p&gt;（3）select支持的文件描述符数量太小了，默认是1024&lt;/p&gt;

&lt;h1 id=&quot;epoll实现&quot;&gt;epoll实现&lt;/h1&gt;

&lt;p&gt;epoll既然是对select和poll的改进，就应该能避免上述的三个缺点。那epoll都是怎么解决的呢？在此之前，我们先看一下epoll和select和poll的调用接口上的不同，select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数，epoll_create,epoll_ctl和epoll_wait，epoll_create是创建一个epoll句柄；epoll_ctl是注册要监听的事件类型；epoll_wait则是等待事件的产生。&lt;/p&gt;

&lt;p&gt;对于第一个缺点，epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时（在epoll_ctl中指定EPOLL_CTL_ADD），会把所有的fd拷贝进内核，而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。&lt;/p&gt;

&lt;p&gt;对于第二个缺点，epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中，而只在epoll_ctl时把current挂一遍（这一遍必不可少）并为每个fd指定一个回调函数，当设备就绪，唤醒等待队列上的等待者时，就会调用这个回调函数，而这个回调函数会把就绪的fd加入一个就绪链表）。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd（利用schedule_timeout()实现睡一会，判断一会的效果，和select实现中的第7步是类似的）。&lt;/p&gt;

&lt;p&gt;对于第三个缺点，epoll没有这个限制，它所支持的FD上限是最大可以打开文件的数目，这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右，具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。&lt;/p&gt;

&lt;h2 id=&quot;总结-1&quot;&gt;总结：&lt;/h2&gt;

&lt;p&gt;（1）select，poll实现需要自己不断轮询所有fd集合，直到设备就绪，期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表，期间也可能多次睡眠和唤醒交替，但是它是设备就绪时，调用回调函数，把就绪fd放入就绪链表中，并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替，但是select和poll在“醒着”的时候要遍历整个fd集合，而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了，这节省了大量的CPU时间。这就是回调机制带来的性能提升。&lt;/p&gt;

&lt;p&gt;（2）select，poll每次调用都要把fd集合从用户态往内核态拷贝一次，并且要把current往设备等待队列中挂一次，而epoll只要一次拷贝，而且把current往等待队列上挂也只挂一次（在epoll_wait的开始，注意这里的等待队列并不是设备等待队列，只是一个epoll内部定义的等待队列）。这也能节省不少的开销。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.cnblogs.com/apprentice89/archive/2013/05/09/3070051.html&quot;&gt;http://www.cnblogs.com/apprentice89/archive/2013/05/09/3070051.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.linuxidc.com/Linux/2012-05/59873p3.htm&quot;&gt;http://www.linuxidc.com/Linux/2012-05/59873p3.htm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://xingyunbaijunwei.blog.163.com/blog/static/76538067201241685556302/&quot;&gt;http://xingyunbaijunwei.blog.163.com/blog/static/76538067201241685556302/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://blog.csdn.net/kkxgx/article/details/7717125&quot;&gt;http://blog.csdn.net/kkxgx/article/details/7717125&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c&quot;&gt;https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/Anker/p/3265058.html&quot;&gt;https://www.cnblogs.com/Anker/p/3265058.html&lt;/a&gt;&lt;/p&gt;</content><author><name>Yangtian</name></author><category term="blog" /><category term="Java" /><category term="IO" /><category term="linux" /><category term="转载" /><summary type="html">select实现</summary></entry><entry><title type="html">Predictors of Radiation Pneumonitis（翻译）</title><link href="https://yangtiancoder.github.io/blog/2018/11/05/ML-Bioinformatics1/" rel="alternate" type="text/html" title="Predictors of Radiation Pneumonitis（翻译）" /><published>2018-11-05T00:00:00+00:00</published><updated>2018-11-05T00:00:00+00:00</updated><id>https://yangtiancoder.github.io/blog/2018/11/05/ML-Bioinformatics1</id><content type="html" xml:base="https://yangtiancoder.github.io/blog/2018/11/05/ML-Bioinformatics1/">&lt;p&gt;首先介绍一下放射性肺炎，放射性肺炎是由于肺癌、乳腺癌、食管癌、恶性淋巴瘤或胸部其他恶性肿瘤经放射治疗后，在放射野内的正常肺组织受到损伤而引起的炎症反应。轻者无症状，炎症可自行消散；重者肺脏发生广泛纤维化，导致呼吸功能损害，甚致呼吸衰竭。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;研究的目的&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;由于对于患有霍奇金和非霍奇金淋巴瘤的病人，在接受放疗之前可能接受过其他的化学治疗等因素，在现代的研究中很少的研究考虑到单一调强放疗（IMRT）和放射性肺炎之间的关系，因此本篇论文研究的内容是调强放疗和放射性肺炎的发病率及临床和剂量学的风险因素。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;方法和材料&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;本论文以2009-2013年150位患有霍奇金和非霍奇金淋巴瘤病人的临床和放射记录作为原始材料，以RTOG（Radiation Therapy Oncology Group）的毒性定义为标准，采用皮尔森卡方检验和多项logistic回归分析的方法处理。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;实验结果&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;对110名患者放射治疗作为巩固治疗，40例患有复发或难治性疾病。全部案例中RP（RTOG等级1-3）的总发生率为14％。对于接受复发或难治性疾病放射治疗的患者（25％）与接受巩固治疗的患者（10％）RP的风险增加。几个剂量学参数预测RP，包括平均肺剂量&amp;gt; 13.5Gy，V20&amp;gt; 30％，V15&amp;gt; 35％，V10&amp;gt; 40％，V5&amp;gt; 55％。其中 V5&amp;gt; 55％的卡方值最高（X^2=19.37）。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在所有的剂量学因素当中，对大体积肺的小剂量处理具有较强的预测能力，其中，对于复发疾病正在接受挽救性化疗的病人以及接受移植前放射性治疗的病人患有放射性肺炎的风险最大。&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;我们已经知道，在初始系统的治疗之后巩固性的放射性治疗有助于提高霍奇金淋巴瘤和非霍奇金淋巴瘤无事件生存率，除此之外，对于复发或难治性疾病患者，放射治疗（RT）可以有效控制局部疾病的恶化，然而一个正在进行的课题是，有关放射性治疗带来的慢性毒性，例如，霍奇金患者淋巴瘤患者中大多数接受过博来霉素，人们的关注的重点是博来霉素和肺部辐射叠加造成的肺毒性的影响。&lt;/p&gt;

&lt;p&gt;放射性肺炎是接受胸部放射治疗剂量的主要的毒性限制。放射性肺炎一般出现在放射性治疗的1-6个月之后，会出现干咳，呼吸困难，低烧和胸痛等症状。&lt;/p&gt;

&lt;p&gt;最开始关于放射性肺炎的预测是建立在非小细胞肺癌的基础上的，Graham等人对99名接受3D适形放疗病人研究后，提出V20的百分比和平均肺剂量和放射性肺炎相关，但是几乎没有出版物讨论体积-剂量指标是否能对淋巴癌进行预测。&lt;/p&gt;

&lt;p&gt;Koh等人发现在2003-2005间的64位病人患有霍奇金淋巴癌具有较低的放射性肺炎的发生率，他们确定36%的V20和14Gy的MLD患有放射性肺炎RTOG2级的概率超过11%。&lt;/p&gt;

&lt;p&gt;由于调强放疗用于肺癌病人的治疗，肺部可以接受更低的肺部接受量例如5，10等，V5v10等也似乎比起V20表现出更强的与放射性肺炎的相关性。据我们所知，还没有研究关于淋巴瘤病人接受放射性治疗的剂量学因素与反射性肺炎之间关系的论文，因此我们的目标是确定在这些病人放射性肺炎的发生率和临床和放射剂量学因素和放射性肺炎的潜在关系。&lt;/p&gt;

&lt;h2 id=&quot;methods-and-materials&quot;&gt;Methods and Materials&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;测量时，总的肺容量不排除GTV,CTV和PTV。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/2015-RP-ICPU.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;证明有说服力，排除其他因素。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;statistical-analysis&quot;&gt;Statistical analysis&lt;/h2&gt;

&lt;p&gt;皮尔森卡方检测用于检验实际频数和理论频数是否较为接近。ROC分析和logistic回归用于评估剂量学因素是否可以预测放射性肺炎。对RP风险和相应剂量阈剂量的最佳值由ROC曲线确定，多项logistic模型确定放射性肺炎的独立预测因子。除此之外，单独的logistic模型每个测试不同的剂量学参数。对于每个模型，获得对应的卡方值测试相对于除了剂量学参数外所有协变量的基线模型的拟合性。P值小于0.05被认为具有统计学意义。&lt;/p&gt;

&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;

&lt;p&gt;表1中列出了研究中包括的150名患者的特征，表2显示治疗细节。在150名患者中，40名患者复发或患顽固疾病并接受补救性化疗，这40人中30人接受自体干细胞移植，6人同种异体干细胞移植或两者都接受有1人。&lt;/p&gt;

&lt;p&gt;21例患者（占整个组的14％）在完成RT后中位数为2.04个月发生肺炎（范围：0.33-9.18个月）。 RP是9级的1级病例，2例2级，10例3级（表3）。 没有患者有4级或5级RP。 所有患者的严重（3级）RP的发生率为6.7％。 在初始化疗后接受放射作为巩固治疗的110例患者中，RP的发生率均为10％。在接受补救性化疗的40例复发或难治性疾病患者中，RP的发生率显着高于25％（P=.019）。&lt;/p&gt;

&lt;p&gt;对于21例发生RP的患者，中位MLD为12.9 Gy（范围：8.2-16.3 Gy），而未发生RP的患者为10.3Gy（范围：3.8-16.0 Gy）。 RP患者（范围：38％-66％）的相应V5为58％，而未患RP的患者为49％（范围：23％-64％）。 分析的其他剂量测定参数列于表4中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/2015-RP-table4.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;表5中列出了潜在预测RP的剂量和临床因素。对于所有检测的剂量学参数（V25，V20，V15，V10，V5和MLD），生成ROC以确定剂量学参数最佳界限值。&lt;/p&gt;

&lt;p&gt;发现预测的唯一临床因素RP的发展是复发或难治性的病史疾病，为其进行移植或抢救化疗（或两者兼而有之）。在接受移植的患者中，在移植前或移植后接受RT的患者的RP率之间没有显着差异（PZ.501）（表5）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/2015-RP-table5.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在逻辑回归中，剂量测定剂量 - 体积和MLD参数仍然显着（表6）。 挽救化疗史（优势比[OR] Z 3.00,95％可信区间[CI]：1.16-7.75，PZ.023）和移植（OR Z 2.71,95％CI：1.04-7.07，PZ.042）保持独立单变量分析中RP的预测因子。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/2015-RP-table6.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在多变量模型中测量每一个可能的剂量学阈值，从V5到V25的每个截止值都是显着的。 然而，似然比c2值为
该模型的最大值包括V5剂量因子，其中V5&amp;gt; 55％（似然比c2 Z 19.37），突出显示该剂量学参数的强度，用于预测肺炎风险的变化（表E1;在线提供的MLD也很强 相反，最低似然比c2值是V20&amp;gt; 30％（似然比c2 Z 8.33）。&lt;/p&gt;

&lt;h2 id=&quot;discussion&quot;&gt;Discussion&lt;/h2&gt;

&lt;p&gt;这种肺损伤机制在使用三维适形RT计划治疗的患者中不明显，因为接受5Gy的肺体积通常不大于接受20Gy的体积。因此与3D适形放疗相比，IMRT中应考虑不同的剂量测定参数。&lt;/p&gt;

&lt;p&gt;因为几乎所有接受补救性化疗的患者都接受了自体或同种异体干细胞移植，所以在本实验中我们无法评估，单独的补救性化疗可以独立的增加相对于移植的风险。除此之外，在目前的研究中，使用的肺容量并未排除任何靶体积，如果使用本报告中确定的阈值，则应考虑这一点。&lt;/p&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;总之，MLD和低剂量参数如V5，V10和V15是用IMRT治疗淋巴瘤患者RP发展的重要预测因子。 接受挽救性化疗并接受造血干细胞移植的复发或难治性疾病患者RP的风险特别高。 无论如何，当使用IMRT治疗霍奇金或非霍奇金淋巴瘤的总肺中&amp;gt; 55％接受5Gy时，RP的风险接近35％。&lt;/p&gt;</content><author><name>Yangtian</name></author><category term="blog" /><category term="Machine Learning" /><category term="论文翻译" /><category term="Bioinformatics" /><summary type="html">首先介绍一下放射性肺炎，放射性肺炎是由于肺癌、乳腺癌、食管癌、恶性淋巴瘤或胸部其他恶性肿瘤经放射治疗后，在放射野内的正常肺组织受到损伤而引起的炎症反应。轻者无症状，炎症可自行消散；重者肺脏发生广泛纤维化，导致呼吸功能损害，甚致呼吸衰竭。</summary></entry><entry><title type="html">继承填坑</title><link href="https://yangtiancoder.github.io/blog/2018/10/30/java-extends/" rel="alternate" type="text/html" title="继承填坑" /><published>2018-10-30T00:00:00+00:00</published><updated>2018-10-30T00:00:00+00:00</updated><id>https://yangtiancoder.github.io/blog/2018/10/30/java-extends</id><content type="html" xml:base="https://yangtiancoder.github.io/blog/2018/10/30/java-extends/">&lt;h2 id=&quot;什么是继承&quot;&gt;什么是继承&lt;/h2&gt;

&lt;p&gt;多个类中存在相同属性和行为时，将这些内容抽取到单独一个类中，那么多个类无需再定义这些属性和行为，只要继承那个类即可。多个类可以称为子类，单独这个类称为父类、超类或者基类。子类可以直接访问父类中的非私有的属性和行为。&lt;/p&gt;

&lt;p&gt;通过 extends 关键字让类与类之间产生继承关系。&lt;/p&gt;

&lt;h2 id=&quot;super和this&quot;&gt;super和this&lt;/h2&gt;

&lt;p&gt;super是一个关键字，代表父类的存储空间标识。(可以理解为父亲的引用)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;使用场景&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当子父类出现同名成员时，可以用super进行区分&lt;/li&gt;
  &lt;li&gt;子类要调用父类构造函数时，可以使用super语句&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;this代表对象的引用(谁调用就代表谁)。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;使用场景&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;引用隐式参数&lt;/li&gt;
  &lt;li&gt;调用该类的其他构造器，可以在参数少的构造器中调用参数多的构造器&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;重写和重载&quot;&gt;重写和重载&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;重写规则&lt;/strong&gt;
   方法重写是存在子父类之间的,子类定义的方法与父类中的方法具有相同的方法名字,相同的参数表（即相同的方法签名）（注意：java 5.0版本以后返回值类型可以为原类型的子类型）&lt;br /&gt;
        (1)子类中不能重写父类中的final方法&lt;br /&gt;
        (2)子类中必须重写父类中的abstract方法   &lt;br /&gt;
        (3)父类私有方法，子类看不到，因此父类私有方法的重写也就无从谈起 &lt;br /&gt;
        (4)覆盖时，子类方法权限一定要大于等于父类方法权限 &lt;br /&gt;
        (5)静态只能覆盖静态&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;重载规则&lt;/strong&gt;
   重载的时候，方法名要一样，但是参数类型和个数不一样，返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。&lt;/p&gt;

&lt;h2 id=&quot;继承之后的执行顺序&quot;&gt;继承之后的执行顺序&lt;/h2&gt;

&lt;p&gt;父类静态代码块→子类静态代码块→父类构造代码块→父类构造方法→子类构造代码块→子类构造方法&lt;/p&gt;

&lt;h2 id=&quot;final关键字&quot;&gt;final关键字&lt;/h2&gt;

&lt;p&gt;final是一个关键字，可以用于修饰类，成员变量，成员方法。&lt;/p&gt;

&lt;p&gt;特点：&lt;/p&gt;

&lt;p&gt;（1）它修饰的类不能被继承。&lt;br /&gt;
（2）它修饰的成员变量是一个常量。&lt;br /&gt;
（3）它修饰的成员方法是不能被子类重写的。&lt;/p&gt;</content><author><name>Yangtian</name></author><category term="blog" /><category term="Java" /><summary type="html">什么是继承</summary></entry><entry><title type="html">卡方检验、COX和ROC等</title><link href="https://yangtiancoder.github.io/blog/2018/10/25/machine-learning-ROC/" rel="alternate" type="text/html" title="卡方检验、COX和ROC等" /><published>2018-10-25T00:00:00+00:00</published><updated>2018-10-25T00:00:00+00:00</updated><id>https://yangtiancoder.github.io/blog/2018/10/25/machine%20learning-ROC</id><content type="html" xml:base="https://yangtiancoder.github.io/blog/2018/10/25/machine-learning-ROC/">&lt;h1 id=&quot;对数秩和检验&quot;&gt;对数秩和检验&lt;/h1&gt;

&lt;p&gt;对数秩和检验是以生存时间的对数为基础推导出来的，其基本思想是实际死亡数和期望死亡数间比较。他对各组生存率做整体比较，故应用范围广。它适用于两组及多组生存率间比较。&lt;/p&gt;

&lt;p&gt;用对数秩和检验对样本的生存率进行比较时。，要求各组生存曲线不能交叉，生存曲线的交叉提示存在某种混杂因素，因此应采用分层的办法或者多因素方法来矫正混杂因素。另外当假设检验推断有差别时，可以通过生存曲线，半数生存期及相对危险度等指标来评价其效果。&lt;/p&gt;

&lt;h1 id=&quot;cox模型&quot;&gt;Cox模型&lt;/h1&gt;

&lt;p&gt;处理多因素生存数据的回归模型-比例危险度模型。&lt;/p&gt;

&lt;p&gt;Cox模型适用于处理单因素或多因素影响下的时间-反应数据，也就是多因素生存分析的数据。这种数据的主要特点是除了有关因素外，每研究对象的随访记录都有两个变量所组成。一个为观察时间一个为结局是否发生。特别适用于随访迟早不一，随访时间长短不一情况处理。&lt;/p&gt;

&lt;p&gt;回归系数 回归系数用来反映因素对生存时间的影响的强度，一般而言，回归系数愈大，则因素对生存时间的影响越大。&lt;/p&gt;

&lt;h1 id=&quot;roc曲线&quot;&gt;ROC曲线&lt;/h1&gt;

&lt;p&gt;在介绍ROC曲线之前，先说说混淆矩阵及两个公式，因为这是ROC曲线计算的基础。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1.混淆矩阵的例子(是否点击广告)：&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/ROC-1.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;说明：&lt;/p&gt;

&lt;p&gt;TP：预测的结果跟实际结果一致，都点击了广告。&lt;/p&gt;

&lt;p&gt;FP：预测结果点击了，但是真实情况是未点击。&lt;/p&gt;

&lt;p&gt;FN：预测结果没有点击，但是真实情况是点击了。&lt;/p&gt;

&lt;p&gt;TN：预测结果没有点击，真实情况也是没有点击。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;2.两个公式：&lt;/p&gt;

    &lt;p&gt;1）真正率：&lt;/p&gt;

    &lt;p&gt;TPR=TP/(TP+FN)&lt;/p&gt;

    &lt;p&gt;2）假正率&lt;/p&gt;

    &lt;p&gt;FPR=FP/(FP+TN)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;3.ROC曲线就是真正率随假正率的变化情况。下面用一段代码展示一下(sklearn包中包含相关算法)：&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;##导入相关包
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sklearn&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;##设置y值：表示实际值
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;##设置pred值：表示预测后的值
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pred&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.35&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;##计算相关数据：注意返回的结果顺序
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thresholds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roc_curve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos_label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;##计算曲线下面积
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roc_auc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;##绘图
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'ROC curve (area = %0.2f)'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roc_auc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'k--'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xlim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ylim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xlabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'False Positive Rate'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ylabel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'True Positive Rate'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;legend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;lower right&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;结果如图所示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/ROC-2.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;4.关于ROC曲线&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1）虚线所示直线随机分类时的ROC曲线，一般画到图中作为参照点&lt;/p&gt;

&lt;p&gt;2）对于一个完美的分类器，ROC曲线应该是从(0,0)到(0,1)，然后横着连到(1,1)的折线&lt;/p&gt;

&lt;p&gt;3）ROC曲线越接近左上角，分类效果越好&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;5.关于AUC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1）AUC表示曲线下面的面积&lt;/p&gt;

&lt;p&gt;2）对于一个完美的分类器，AUC的值应该为1&lt;/p&gt;

&lt;p&gt;3）对于一个随机猜测分类器(即图中虚直线)，AUC的面积为0.5&lt;/p&gt;

&lt;p&gt;4）AUC面积越大，分类效果越好&lt;/p&gt;

&lt;h1 id=&quot;卡方检验&quot;&gt;卡方检验&lt;/h1&gt;

&lt;h2 id=&quot;什么是卡方检验&quot;&gt;什么是卡方检验&lt;/h2&gt;

&lt;p&gt;卡方检验是一种用途很广的计数资料的假设检验方法。它属于非参数检验的范畴，主要是比较两个及两个以上样本率( 构成比）以及两个分类变量的关联性分析。其根本思想就是在于比较理论频数和实际频数的吻合程度或拟合优度问题。&lt;/p&gt;

&lt;p&gt;它在分类资料统计推断中的应用包括：两个率或两个构成比比较的卡方检验；多个率或多个构成比比较的卡方检验以及分类资料的相关分析等。&lt;/p&gt;

&lt;h2 id=&quot;举例一以下为一个典型的四格卡方检验我们想知道喝牛奶对感冒发病率有没有影响&quot;&gt;举例一：以下为一个典型的四格卡方检验，我们想知道喝牛奶对感冒发病率有没有影响：&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/kafang-1.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过简单的统计我们得出喝牛奶组和不喝牛奶组的感冒率为30.94%和25.00%，两者的差别可能是抽样误差导致，也有可能是牛奶对感冒率真的有影响。&lt;/p&gt;

&lt;p&gt;为了确定真实原因，我们先假设喝牛奶对感冒发病率是没有影响的，即喝牛奶喝感冒时独立无关的，所以我们可以得出感冒的发病率实际是（43+28）/（43+28+96+84）= 28.29%&lt;/p&gt;

&lt;p&gt;所以，理论的四格表应该如下表所示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/kafang-2.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;即下表：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/kafang-3.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果喝牛奶喝感冒真的是独立无关的，那么四格表里的理论值和实际值差别应该会很小。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;卡方检验的计算公式为：&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/kafang-4.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中，A为实际值，T为理论值。&lt;/p&gt;

&lt;p&gt;x2用于衡量实际值与理论值的差异程度（也就是卡方检验的核心思想），包含了以下两个信息：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;实际值与理论值偏差的绝对大小（由于平方的存在，差异是被放大的）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;差异程度与理论值的相对大小&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;根据卡方检验公式我们可以得出例1的卡方值为：&lt;/p&gt;

&lt;p&gt;卡方 = (43 - 39.3231)平方 / 39.3231 + (28 - 31.6848)平方 / 31.6848 + (96 - 99.6769)平方 / 99.6769 + (84 - 80.3152)平方 / 80.3152 = 1.077&lt;/p&gt;

&lt;p&gt;卡方分布的临界值&lt;/p&gt;

&lt;p&gt;上一步我们得到了卡方的值，但是如何通过卡方的值来判断喝牛奶和感冒是否真的是独立无关的？也就是说，怎么知道无关性假设是否可靠？&lt;/p&gt;

&lt;p&gt;答案是，通过查询卡方分布的临界值表。&lt;/p&gt;

&lt;p&gt;这里需要用到一个自由度的概念，自由度等于V = (行数 - 1) * (列数 - 1)，对四格表，自由度V = 1。
对V = 1，喝牛奶和感冒95%概率不相关的卡方分布的临界概率是：3.84。即如果卡方大于3.84，则认为喝牛奶和感冒有95%的概率不相关。&lt;/p&gt;

&lt;p&gt;显然1.077&amp;lt;3.84，没有达到卡方分布的临界值，所以喝牛奶和感冒独立不相关的假设不成立。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;卡方分布表&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/Yangtiancoder/Yangtiancoder.github.io/blob/master/assets/images/kafang-5.png?raw=true&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/ludan_xia/article/details/81737669&quot;&gt;https://blog.csdn.net/ludan_xia/article/details/81737669&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.cnblogs.com/dlml/p/4403482.html&quot;&gt;https://www.cnblogs.com/dlml/p/4403482.html&lt;/a&gt;&lt;/p&gt;</content><author><name>Yangtian</name></author><category term="blog" /><category term="Machine Learning" /><summary type="html">对数秩和检验</summary></entry></feed>