-
Notifications
You must be signed in to change notification settings - Fork 1
/
atom.xml
172 lines (104 loc) · 133 KB
/
atom.xml
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>FengZhao's Blog</title>
<subtitle>「年华易逝 懂得珍惜」</subtitle>
<link href="https://fengzhao.me/atom.xml" rel="self"/>
<link href="https://fengzhao.me/"/>
<updated>2023-10-17T03:28:58.139Z</updated>
<id>https://fengzhao.me/</id>
<author>
<name>fengzhao</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>一些组网常识和方法</title>
<link href="https://fengzhao.me/2022/08/22/2022-08-22-%E4%B8%80%E4%BA%9B%E7%BB%84%E7%BD%91%E5%B8%B8%E8%AF%86%E5%92%8C%E6%96%B9%E6%B3%95/"/>
<id>https://fengzhao.me/2022/08/22/2022-08-22-%E4%B8%80%E4%BA%9B%E7%BB%84%E7%BD%91%E5%B8%B8%E8%AF%86%E5%92%8C%E6%96%B9%E6%B3%95/</id>
<published>2022-08-22T03:27:59.000Z</published>
<updated>2023-10-17T03:28:58.139Z</updated>
<content type="html"><![CDATA[<h1 id="网络常识基础"><a href="#网络常识基础" class="headerlink" title="网络常识基础"></a>网络常识基础</h1><h3 id="网速知识"><a href="#网速知识" class="headerlink" title="网速知识"></a>网速知识</h3><p><strong>下载/上传</strong>:</p><p>网络数据传输分为发送数据和接收数据两部分:</p><ul><li><strong>上传</strong>就是向外部发送数据(上行)</li><li><strong>下载</strong>就是从外部接收数据(下行)</li></ul><p><strong>带宽单位</strong></p><span id="more"></span><p>日常我们与网络运营商签约宽带服务,一般都喜欢讲百兆带宽,一百兆(100M),两百兆,其实这是一个速度单位。表示单位时间内传输的数据量(每秒)。</p><p>在计算机网络或网络运营商中,100兆的准确描述应该是 100 Mbps,就是就是 Mega bits per second </p><p>在计算机中,数据容量的单位是大家常见的:B,KB,MB,GB。这个B是指 bytes 。</p><pre class="line-numbers language-none"><code class="language-none">1 bytes = 8 bits100 Mbps = 100/8 MB/s = 12.5 MB/s<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p><strong>百兆宽带,理论上的下载速度,应该可以达到上述值,但是要考虑网络传输各种信号衰减和延迟,实际上可能达不到这个理想环境。</strong></p><p><strong>带宽和延迟的专业说法</strong></p><p><strong>更专业一点说,带宽是指在单位时间(一般指的是1秒钟)内能传输的数据量</strong>,是几乎所有互联网服务提供商在商业和营销中称之为”速度”的东西。</p><p>但是<strong>实际上带宽不等于速度</strong>,当你听到有人说“我的网速是30 Mbps”或类似的东西时,他们实际指的是他们的互联网服务的带宽容量,而不是速度。<strong>网络的速度实际上是带宽和延迟的结果</strong>。</p><p>带宽(bandwidth)也称为吞吐量(throughput),是指在一个固定的时间内(1秒),从网络一端流到另一端的最大数据位数,也可以理解为网络的两个节点之间特定数据流的平均速率。带宽的单位是比特/秒(bit/s,简写为bps)。</p><p>带宽可以用高速公路做比喻来帮助理解它的含义:高速公路的车道可以衡量传输的能力。公路的宽度好比是带宽,行驶在公路上的汽车就好比是网络传输的数据。</p><p>车道越多,车辆通行能力就越强,发生堵车的概率就越低。同样的,拥有更宽的带宽,也就是有更大的数据传输能力。</p><p>带宽 bandwidth :<strong>带宽是指在单位时间(一般指的是1秒钟)内能传输的数据量</strong>。</p><p>延迟 delay:数据从一个节点到另外一个节点所经历的时间,通常以 ms 为单位。</p><h4 id="中国网络运营商常见问题"><a href="#中国网络运营商常见问题" class="headerlink" title="中国网络运营商常见问题"></a>中国网络运营商常见问题</h4><p>随着移动互联网的兴起、“云时代”的到来,把文件存储在网盘、把拍好的照片、视频分享到网上,已成为网民越来越普遍的需求。</p><p>网速快慢,不仅指下载速度,还需要更多需要提高上传速率。</p><p>而恰恰是在上传速率上,中国的宽带运营商显露出了严重的不足——上下行不对称已经形成默认的潜规则,用户的上行带宽远远低于下行带宽。</p><p>ADSL时代,上行下行不对称是技术问题,ADSL(Asymmetric Digital Subscriber Loop)技术是一种不对称数字用户线实现宽带接入互连网的技术,它采用频分复用技术把普通的电话线分成了电话、上行和下行三个相对独立的信道,从而避免了相互之间的干扰,一根线缆内多条电线上的对称信号会显著地限制数据传输速率与线缆的有效通信长度,在大多数情况下,其下行与上行带宽之比可达到10:1的比率。</p><p>到了光纤时代,技术上是不存在不对称问题的,光纤具有频带宽、容量大、信号质量好、可靠性高等特点,是目前宽带业务发展的方向。是利用两条光纤分别负责上行和下行,不存在ADSL上下行不对称的技术问题,以PON技术为例,下行和上行是频分复用,互不影响,并且因为光纤传输的原理,即使你家离局端20公里,速率也不会有太大变化。</p><p>从拨号上网的ADSL时代发展到光纤,技术上已经解决了宽带网络的上下行速率不对等问题。但是实际上,光纤用户依然被宽带运营商限制了上行带宽,即使是光纤入户(FTTH)的百兆带宽,上传带宽也不足4M(上传速度最大为512K/s)。</p><p>他们都受网络带宽和设备性能制约。 在日常网络传输中大致1Mbps=1024/8Kb/s=128Kb/s(1/8)。</p><p>例如上行的网络带宽为100Mbps,那么最大上传速度就是12800Kb/s,也就是12.5Mb/s</p><p><strong>国内家庭宽带速率的现状</strong></p><p>如今家庭宽带已经普及100M宽带,而200M,500M甚至1000M的宽带也慢慢开始在各城市推广上线。根据工信部《工业和信息化部公告》〔2018年第54号〕</p><p><strong>标准规定:公众用户固定宽带接入业务,当下行小于等于150M时,签约上行接入速率与签约下行接入速率按照最低1:5的比例配置,当下行大于150M时,签约上行速率不低于30M。</strong></p><p>从2019年1月1日起生效</p><p>如果你家是电信200M用户,那么理应上行速率为30M,转换为上传速度,理论上满速约为4M/s,外网访问内网在线播放1080P电影实测流畅。</p><p>绝大部分宽带运营商为家庭用户提供的宽带为内网模式,即由运营商提供的光猫设备管理拨号和路由服务,而用户只能获取内网IP,同时运营商封闭80/443端口,以至于外网无法更快更便捷的访问到家庭内网中的设备。</p><p><strong>目前家庭环境中主流的”外网访问内网”分为<code>公网DDNS</code>和<code>内网穿透</code>两种方式。</strong></p><p>公网DDNS是指利用DDNS动态域名解析技术,在外网通过域名访问家庭宽带的公网IP,由路由器提供端口转发,与内网设备直接通讯。</p><p>使用公网DDNS直连方式需要满足<strong>公网IP</strong>,<strong>桥接模式</strong>,<strong>端口映射</strong>三个前提,<strong>缺一不可</strong>。然后利用<strong>DDNS动态域名解析</strong>技术使用域名访问。</p><p><strong>公网IP</strong></p><p>公网IP是外网访问内网最关键因素。有公网IP,通过端口映射,用户在外网可以和家庭内网的设备直连,速度能达到上行带宽的上限,体验最佳。</p><p>内网IP是由运营商光猫获取分配,外网无法与内网设备直连,只能通过内网穿透服务来代理访问内网的设备,然而内网穿透服务依赖于第三方服务器的带宽,所以体验略差。</p><p>通过联系运营商,申请开放公网IP,并申请把运营商光猫改为桥接模式,根据提供的宽带账号密码。由用户在自己的路由器或防火墙中设置PPPOE拨号,路由器即取到了获取公网IP。然后通过路由器的端口映射功能,把对公网IP的端口访问转到内部的端口。</p><p>端口映射功能,可以将路由器 WAN IP 的一个端口映射到局域网中的一台计算机上(当前的路由器支持IPV4的网络端口映射,暂不支持IPV6)。</p><p>当因特网用户访问该 IP 的该端口时(如从因特网实时访问家庭网络摄像头等),路由器将会自动将该请求映射到已指定的计算机上,并通过该计算机对外提供服务。</p><p>同时也要注意,如果自己的核心网络设备取到了公网IP,那也一定要注意信息安全,这意味着内网的服务可能会被外网任何人和任何地址访问和扫描,所以有必要重视信息安全工作。</p><p>外网访问内网设备的基础是通过IP来通讯,而且运营商提供的公网IP为动态IP,IP地址会定时更变(比如光猫重启等),所以我们需要使用DDNS服务来使域名绑定随时变化的动态公网IP。</p><p>DDNS服务是指让公网IP在更变后立即通知域名服务商更变A记录指向到新IP,保证域名指向的持续性,使域名访问不受影响。DDNS大多都是免费提供。</p><p><strong>DDNS</strong></p><p>大部分路由器(包括非智能路由)都支持ddns,一般是集成花生壳、公云等有限的几家服务商。直接使用路由器集成的ddns功能,优点是简单,缺点就是慢。</p><p>因为DDNS都是用服务商提供的域名,如果要使用我们自己的域名,需要cname到服务商的域名,这里就存在二次解析,更别说免费DDNS本身速度慢了。</p><p>还有另外一种方式,可以在内网一台机器上运行一个服务,然后会通过调用阿里云api的方式,实现 ddns。</p><p>原理大概是通过这个服务定时任务去获取本地网络的公网出口IP,然后通过阿里云api的方式去将自己的域名解析到这个公网IP(根据出口ip动态的调修改解析)。</p><p>比如:<a href="https://github.com/jeessy2/ddns-go">https://github.com/jeessy2/ddns-go</a></p><h1 id="公网IP的管理机构"><a href="#公网IP的管理机构" class="headerlink" title="公网IP的管理机构"></a>公网IP的管理机构</h1><p><strong>负责全球IP地址分配的机构(ICANN)</strong></p><p>Internet<a href="https://www.zhihu.com/search?q=%E7%BD%91%E7%BB%9C%E5%8D%8F%E4%BC%9A%E7%BD%91%E7%BB%9C%E5%9C%B0%E5%9D%80%E5%88%86%E9%85%8D%E5%A7%94%E5%91%98%E4%BC%9AICAN&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22:%22article%22,%22sourceId%22:356391511%7D">网络协会网络地址分配委员会ICAN</a>N(TheInternet Corporation for Assigned Names and Numbers),负责全球IP地址分配的专门机构,位于美国加利福尼亚州。</p><p>ICANN实行分级管理,TA将IP地址分配给地区级Internet注册机构RIR(RegionalInternet Registry),然后由RIR负责该地区的注册服务。</p><p>管理 ASN 和 IP 的机构就有 <a href="https://zh.wikipedia.org/wiki/%E5%8C%BA%E5%9F%9F%E4%BA%92%E8%81%94%E7%BD%91%E6%B3%A8%E5%86%8C%E7%AE%A1%E7%90%86%E6%9C%BA%E6%9E%84">APNIC、AfriNIC、RIPE、ARIN、LACNIC 五家 RIR(地区注册局)</a>,根据大洲决定各自的管辖范围。</p><ul><li><p>ARIN:AmericanRegistry for Internet Numbers,即<a href="https://www.zhihu.com/search?q=%E7%BE%8E%E5%9B%BDInterne&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22:%22article%22,%22sourceId%22:356391511%7D">美国Interne</a>t号码注册管理机构,位于美国<a href="https://www.zhihu.com/search?q=%E5%BC%97%E5%90%89%E5%B0%BC%E4%BA%9A%E5%B7%9E&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22:%22article%22,%22sourceId%22:356391511%7D">弗吉尼亚州</a>,负责美国、加拿大、<a href="https://www.zhihu.com/search?q=%E6%92%92%E5%93%88%E6%8B%89%E6%B2%99%E6%BC%A0&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22:%22article%22,%22sourceId%22:356391511%7D">撒哈拉沙漠</a>以及南非州的IP地址。</p></li><li><p>RIPE Ncc:RIPE Network Coordination Centre,即RIPE网络协调中心,位于荷兰阿姆斯特丹,负责欧洲、北非、西亚、俄罗斯亚洲领土的IP地址。</p></li><li><p>APNIC:Asia-PacificNetwork Information Centre,即亚洲与太平洋地区网络信息中心,位于澳大利亚的<a href="https://www.zhihu.com/search?q=%E5%B8%83%E9%87%8C%E6%96%AF%E7%8F%AD&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22:%22article%22,%22sourceId%22:356391511%7D">布里斯班</a>,负责亚太地区64个经济体的IP地址。</p></li><li><p>LACNIC:LatinAmerican and Caribbean Internet Address Registry,即<a href="https://www.zhihu.com/search?q=%E6%8B%89%E4%B8%81%E7%BE%8E%E6%B4%B2&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22:%22article%22,%22sourceId%22:356391511%7D">拉丁美洲</a>、加勒比IP地址注册中心,位于<a href="https://www.zhihu.com/search?q=%E5%B7%B4%E8%A5%BF%E5%9C%A3%E4%BF%9D%E7%BD%97&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22:%22article%22,%22sourceId%22:356391511%7D">巴西圣保罗</a>,负责拉丁美洲、加勒比海诸岛的IP地址。</p></li><li><p>AFRINIC:AfricanNetwork Information Centre,即非洲网络信息中心,位于<a href="https://www.zhihu.com/search?q=%E5%8D%97%E9%9D%9E%E6%AF%94%E5%8B%92%E9%99%80%E5%88%A9%E4%BA%9A&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22:%22article%22,%22sourceId%22:356391511%7D">南非比勒陀利亚</a>,负责非洲中心地区的IP地址。</p></li></ul><p>中国IP地址分配是由<strong>中国互联网信息中心(CNNIC)</strong>负责,CNNIC是亚太互联网络信息中心(APNIC)下一级机构,是国家级IP地址注册机构成员(NIR)。 </p><p>该机构位于中国科学院计算机网络信息中心内。中国各地互联网服务商(ISP)又是其下一级机构 ,只要他们申请加入CNNIC分配联盟,就可申请到IP地址。</p><p>普通个人和机构一般向服务商(各种ISP:如电信、移动、联通)申请公网IPv4地址。</p><p>在我国,公网IP地址和网络等资源,主要是由电信联通移动三大网络运营商,以及一些云计算公司(阿里云,腾讯云,华为云,金山云,青云,Ucloud等)掌握。</p><p>当然,普通的企业也是可以申请公网IP的,由于公网IPv4的宽带价格非常贵,小型企业,个人用户,家庭用户都用不到。</p><p>比如,在阿里云腾讯云等场景中,我们购买云服务器,可以选择购买几个不带公网IP的服务器,组成云上的内网集群应用,数据库,WEB,Nginx 等。</p><p>做为企业用户和个人用户,一般我们要找运营商来申请网络。综合来看,根据ip地址类型,isp给我们分配大概有三种类型:</p><ul><li><p>我们拨号的核心网络设备(路由器或防火墙)上取到的IP是内网IP,比如我司之前取到的100.这种,这种情况一般是由于运营商提供的光猫设备管理拨号</p></li><li><p>我们核心网络设备(路由器)取到的ip是公网IP,不考虑网站备案,isp恶意封禁等情况下,理论上,我们可以拨号设备上做NAT端口映射后,我们把内部的网站,windows mstsc,linux openssh等等服务都可以通过这个ip暴露出来,一般不重启设备的话,这个ip基本上不会变。</p></li><li><p>静态公网ip,这种宽带最好,这个ip你独享,而且一般上下行对等,一般用于建站,在idc机房内使用。</p></li></ul><h1 id="NAT端口映射"><a href="#NAT端口映射" class="headerlink" title="NAT端口映射"></a>NAT端口映射</h1><p>NAT有三种类型:</p><p>静态NAT(Basic NAT):此类NAT在本地和外网地址之间做一到一的永久映射。因此要维护一个公网的地址池。</p><p>须注意静态NAT要求用户对每一台主机都有一个真实的Internet IP地址。</p><p>(静态NAT实际使用场景很少见到,我们申请一个带公网IP的云服务器,就是这种例子)</p><p>动态转换 (Dynamic Nat) 是指将内部网络的私有IP地址在经过网关等NAT设备时转换为公用IP地址时,公网IP地址是不确定的,而是随机的。</p><p>也就是说在NAT网关上维护了一个公网IP的地址池,当私网地址访问外网时,IP地址是从公网地址池中动态获取的。</p><p>所有被授权访问上Internet的私有IP地址可随机转换为任何指定的合法IP地址。</p><p>也就是说,只要指定哪些内部地址可以进行转换,以及用哪些合法地址作为外部地址时,就可以进行动态转换。</p><p>动态转换可以使用多个合法外部地址集。当ISP提供的合法IP地址略少于网络内部的计算机数量时。可以采用动态转换的方式。 </p><p>(动态NAT其实就是私网IP和公网IP的多对多的映射关系)</p><p>端口NAT(PAT):最为流行的NAT配置类型。通过多个源端口,将多个私网IP映射到一个公网IP(多到一)。</p><p>使用PAT能够使上千个用户仅使用一个公网IP地址连接到Internet。</p><p>(端口NAT是用法最广的NAT,目前在运营商中也是被广泛使用)</p><p>根据收发报文的不同,NAT又可以分为以下两种:</p><p>SNAT(源地址转换):对于客户端,发出去的IP报文,经过NAT网关设备时,NAT网关会把源IP地址转换为NAT网关的IP地址。</p><p>使得内网主机发出的数据包能够到达外网主机。</p><p>应用场景:让一些内网服务器可以访问互联网</p><p>DNAT(目的地址转换):当IP报文从互联网发到NAT网关时,只会返回到NAT网关设备。所以NAT网关必须要记录,对应的报文,</p><p>应用场景:让不在外网的机器,也可以对互联网提供服务,有点像反向代理一样。</p><p>比如我们的后端服务器和数据库可能都是在内网,通过DNAT将服务暴露在互联网</p>]]></content>
<summary type="html"><h1 id="网络常识基础"><a href="#网络常识基础" class="headerlink" title="网络常识基础"></a>网络常识基础</h1><h3 id="网速知识"><a href="#网速知识" class="headerlink" title="网速知识"></a>网速知识</h3><p><strong>下载&#x2F;上传</strong>:</p>
<p>网络数据传输分为发送数据和接收数据两部分:</p>
<ul>
<li><strong>上传</strong>就是向外部发送数据(上行)</li>
<li><strong>下载</strong>就是从外部接收数据(下行)</li>
</ul>
<p><strong>带宽单位</strong></p></summary>
<category term="计算机网络" scheme="https://fengzhao.me/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
</entry>
<entry>
<title>理解HTTPS中的htst</title>
<link href="https://fengzhao.me/2022/02/22/2022-02-22-%E7%90%86%E8%A7%A3HTTPS%E4%B8%AD%E7%9A%84htst/"/>
<id>https://fengzhao.me/2022/02/22/2022-02-22-%E7%90%86%E8%A7%A3HTTPS%E4%B8%AD%E7%9A%84htst/</id>
<published>2022-02-22T09:43:46.000Z</published>
<updated>2022-08-22T03:12:26.642Z</updated>
<content type="html"><![CDATA[<h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>有不少网站只通过HTTPS对外提供服务,但用户在访问某个网站的时候,在浏览器里却往往直接输入网站域名(例如 <a href="http://www.example.com/">www.example.com</a> ),甚至都不带www前缀,而不是输入完整的URL(例如 <a href="https://www.example.com/">https://www.example.com</a> )。不过浏览器依然能正确的使用HTTPS发起请求。这背后多亏了服务器和浏览器的协作。</p><p>现代浏览器,地址栏(多功能框)也可以用作搜索框。地址栏会自动使用 Google 进行搜索,但您也可将默认搜索引擎改设为另一搜索引擎。其实当我们在浏览器地址栏开始输入信息时,浏览器就已经开始进行工作了。</p><span id="more"></span><p>首先它会监听我们输入的信息并尝试匹配出你想要访问的网址或关键词,以 <code>chrome浏览器</code> 为例,它会猜我们想要什么,给出下面的建议项:</p><ul><li><p>使用默认搜索引擎搜索关键字</p></li><li><p>书签,历史记录和最近下载中存储的其他链接</p></li><li><p>使用默认搜索引擎的相关关键字搜索选项</p></li></ul><p>这些建议选项来源于输入内容匹配到的书签和历史记录(URL和title都可被匹配),输入关键字的搜索建议,以及其它的一些策略。Chrome提供了一个工具页<code>chrome://predictors</code>来查看历史建议。可以看出,每个建议会被计算命中次数和命中概率。如果输入的内容匹配到较高预测分值的建议项时,Chrome根据预测分值的高低会有不同的处理策略。<br>如在输入过程中就提前进行<code>DNS预解析</code>,预先建立<code>TCP连接</code>,甚至<code>预先加载页面</code>等。这样,当我们输入完URL按回车键时,Chrome其实已经提前开始工作了,以便为用户节省时间。</p><p>简单来讲,浏览器向网站发起一次HTTP请求,在得到一个重定向响应后,发起一次HTTPS请求并得到最终的响应内容。所有的这一切对用户而言是完全透明的,所以在用户眼里看来,在浏览器里直接输入域名却依然可以用HTTPS协议和网站进行安全的通信,是个不错的用户体验。</p><p>比如在 Nginx 中常见的强制 HTTP 跳转到 HTTPS,其实就是利用了 HTTP 的301跳转,参考<a href="https://cloud.tencent.com/document/product/400/35244">腾讯云配置</a></p><p>如图:</p><p><img src="/redirect.jpeg" alt="重定向"></p><h2 id="SSL剥离攻击"><a href="#SSL剥离攻击" class="headerlink" title="SSL剥离攻击"></a>SSL剥离攻击</h2><p>上面的一切看上去都是那么的完美,但其实不然,由于在建立起HTTPS连接之前存在一次明文的HTTP请求和重定向(上图中的第1、2步),使得攻击者可以以中间人的方式劫持这次请求,从而进行后续的攻击,例如窃听数据,篡改请求和响应,跳转到钓鱼网站等。</p><p>以劫持请求并跳转到钓鱼网站为例,其大致做法如下图所示(劫持HTTP请求,阻止HTTPS连接,并进行钓鱼攻击):</p><ul><li>第1步:浏览器发起一次明文HTTP请求,但实际上会被攻击者拦截下来。</li><li>第2步:攻击者作为代理,把当前请求转发给钓鱼网站。</li><li>第3步:钓鱼网站返回假冒的网页内容。</li><li>第4步:攻击者把假冒的网页内容返回给浏览器。</li></ul><p>这个攻击的精妙之处在于,攻击者直接劫持了HTTP请求,并返回了内容给浏览器,根本不给浏览器同真实网站建立HTTPS连接的机会。<br>因此浏览器会误以为真实网站就是通过HTTP对外提供服务,自然也就不会向用户报告当前的连接不安全。于是攻击者几乎可以神不知鬼不觉的对请求和响应动手脚。</p><p>既然建立HTTPS连接之前的这一次HTTP明文请求和重定向有可能被攻击者劫持,那么解决这一问题的思路自然就变成了如何避免出现这样的HTTP请求。</p><p>我们期望的浏览器行为是:<strong>当用户让浏览器发起HTTP请求的时候,浏览器内部将其转换为HTTPS请求,直接略过上述的HTTP请求和重定向,从而使得中间人攻击失效,规避风险。</strong></p><h2 id="HSTS技术"><a href="#HSTS技术" class="headerlink" title="HSTS技术"></a>HSTS技术</h2><p>那么问题来了,浏览器是如何做到这一点的呢?它怎么知道哪个域名应该发HTTPS请求,那个域名应该用HTTP请求呢?</p><p>HSTS技术就这样被引进来了。<strong>HSTS(HTTP Strict Transport Security)是国际互联网工程组织 IETF 发布的一种互联网安全策略机制(web security policy mechanism)。采用HSTS策略的网站将保证浏览器始终连接到该网站的HTTPS加密版本,不需要用户手动在URL地址栏中输入加密地址,以减少会话劫持风险。HSTS 最早于2015年被纳入到 ThoughtWorks 技术雷达,并且在2016年的最新一期技术雷达里,它直接从“评估(Trial)”阶段进入到了“采用(Adopt)“阶段,这意味着ThoughtWorks强烈主张业界积极采用这项安全防御措施,并且ThoughtWorks已经将其应用于自己的项目。</strong></p><p>HSTS 最为核心的是一个HTTP响应头(HTTP Response Header)。正是它可以让浏览器得知,在接下来的一段时间内,当前域名只能通过HTTPS进行访问,并且在浏览器发现当前连接不安全的情况下,强制拒绝用户的后续访问要求。</p><p>正是它可以让浏览器得知,在接下来的一段时间内,当前域名只能通过HTTPS进行访问,并且在浏览器发现当前连接不安全的情况下,强制拒绝用户的后续访问要求。</p><p>HSTS Header的语法如下:</p><pre class="line-numbers language-http" data-language="http"><code class="language-http"><span class="token header"><span class="token header-name keyword">Strict-Transport-Security</span><span class="token punctuation">:</span> <span class="token header-value hsts languages-hsts"><max-age=>[; includeSubDomains][; preload]</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><ul><li><p>max-age是必选参数,是一个以秒为单位的数值,它代表着HSTS Header的过期时间,通常设置为1年,即31536000秒。</p></li><li><p>includeSubDomains是可选参数,如果包含它,则意味着当前域名及其子域名均开启HSTS保护。</p></li><li><p>preload是可选参数,只有当你申请将自己的域名加入到浏览器内置列表的时候才需要使用到它。关于浏览器内置列表,下文有详细介绍。</p></li></ul><h3 id="让浏览器直接发起HTTPS请求"><a href="#让浏览器直接发起HTTPS请求" class="headerlink" title="让浏览器直接发起HTTPS请求"></a>让浏览器直接发起HTTPS请求</h3><p>只要在服务器返回给浏览器的响应头中,增加Strict-Transport-Security这个HTTP Header(下文简称HSTS Header),例如:</p><pre class="line-numbers language-none"><code class="language-none">Strict-Transport-Security: max-age=31536000; includeSubDomains<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>就可以告诉浏览器,在接下来的31536000秒内(1年),对于当前域名及其子域名的后续通信应该强制性的只使用HTTPS,直到超过有效期为止。</p><p>Chrome、Firefox 等浏览器里,当您尝试访问该域名下的内容时,会产生一个 307 Internal Redirect(内部跳转),自动跳转到 HTTPS 请求。</p><p>注意这段话,Chrome 访问该域名时,会产生一个 307 的内部跳转,并自动重定向到该地址的 HTTPS 版本。</p><p>这个 307 响应是虚假的(dummy),而非服务器生成的——即 Chrome 是先在内部进行了此操作,然后才发出真正到达目标服务器的 HTTPS 请求。<br>(注意HTTP规范中的 307 状态码描述是 Internal Redirect,而 307 状态码本身的描述是 Temporary Redirect)</p><p>很多地方都可以进行HSTS的配置,例如反向代理服务器、应用服务器、应用程序框架,以及应用程序中自定义Header。你可以根据实际情况进行选择。<br>常见的是在代理服务器中进行配置,以Nginx为例,只需在配置文件中加上下面这条指令即可:</p><pre class="line-numbers language-SHELL" data-language="SHELL"><code class="language-SHELL"># 在 Nginx的HTTPS的server块下面添加这个头部add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>如图:</p><p><img src="/307.jpg" alt="307"></p><p>只要是在有效期内,浏览器都将直接强制性的发起HTTPS请求,但是问题又来了,有效期过了怎么办?其实不用为此过多担心。</p><p>因为 HSTS Header 存在于每个响应中,随着用户和网站的交互,这个有效时间时刻都在刷新,再加上有效期通常都被设置成了1年。</p><p>所以只要用户的前后两次请求之间的时间间隔没有超过1年,则基本上不会出现安全风险。</p><p>更何况,就算超过了有效期,但是只要用户和网站再进行一次新的交互,用户的浏览器又将开启有效期为1年的HSTS保护。</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 看一下京东网站的案例,访问http站,重定向到https站点,且返回了HSTS响应头,如果是PC浏览器,下次访问就直接内部307了</span><span class="token punctuation">[</span>root@compute-share-stxz ~<span class="token punctuation">]</span><span class="token comment">#</span><span class="token punctuation">[</span>root@compute-share-stxz ~<span class="token punctuation">]</span><span class="token comment"># curl -I http://www.jd.com</span>HTTP/1.1 <span class="token number">302</span> Moved TemporarilyServer: nginxDate: Mon, <span class="token number">22</span> Aug <span class="token number">2022</span> 03:09:17 GMTContent-Type: text/htmlContent-Length: <span class="token number">138</span>Connection: keep-aliveLocation: https://www.jd.com/Timing-Allow-Origin: *X-Trace: <span class="token number">302</span>-1661137757974-0-0-0-0-0Strict-Transport-Security: max-age<span class="token operator">=</span><span class="token number">3600</span><span class="token punctuation">[</span>root@compute-share-stxz ~<span class="token punctuation">]</span><span class="token comment">#</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="强制拒绝非HTTPS请求"><a href="#强制拒绝非HTTPS请求" class="headerlink" title="强制拒绝非HTTPS请求"></a>强制拒绝非HTTPS请求</h3><p>在没有HSTS保护的情况下,当浏览器发现当前网站的证书出现错误,或者浏览器和服务器之间的通信不安全,无法建立HTTPS连接的时候,浏览器通常会警告用户,但是却又允许用户继续不安全的访问。(比如使用自签证书的网站,但是客户端电脑上又没有添加信任的CA)</p><p>如下图所示,用户可以点击图中红色方框中的链接,继续在不安全的连接下进行访问。(浏览器依然允许用户进行不安全的访问)</p><p>理论上而言,用户看到这个警告之后就应该提高警惕,意识到自己和网站之间的通信不安全,可能被劫持也可能被窃听,如果访问的恰好是银行、金融类网站的话后果更是不堪设想,理应终止后续操作。<br>然而现实很残酷,就我的实际观察来看,有不少用户在遇到这样的警告之后依然选择了继续访问。</p><p>不过随着HSTS的出现,事情有了转机。对于启用了浏览器HSTS保护的网站,如果浏览器发现当前连接不安全,它将仅仅警告用户,而不再给用户提供是否继续访问的选择,从而避免后续安全问题的发生。</p><p>例如,当访问Google搜索引擎的时候,如果当前通信连接存在安全问题,浏览器将会彻底阻止用户继续访问Google,如下图所示。</p><p><img src="/6-insecurity-visit.jpg" alt="insecurity-visit">)</p><h2 id="HSTS-Preload-List"><a href="#HSTS-Preload-List" class="headerlink" title="HSTS Preload List"></a>HSTS Preload List</h2><p>HSTS存在一个比较薄弱的环节,那就是浏览器没有当前网站的HSTS信息的时候,或者第一次访问网站的时候,依然需要一次明文的HTTP请求和重定向才能切换到HTTPS,以及刷新HSTS信息。</p><p>而就是这么一瞬间却给攻击者留下了可乘之机,使得他们可以把这一次的HTTP请求劫持下来,继续中间人攻击。(还是存在被攻击的可能性)</p><p>针对上面的攻击,HSTS也有应对办法,那就是在<strong>浏览器里内置一个列表HSTS Preload List</strong> ,只要是在这个列表里的域名,无论何时、何种情况,浏览器访问它们都只会使用HTTPS发起连接。</p><p>这个列表由Google Chromium维护,FireFox、Safari、IE等主流浏览器均在使用。</p><p><strong>不过需要特别注意的是,在生产环境下使用HSTS应当特别谨慎,因为一旦浏览器接收到HSTS Header(假如有效期是1年),但是网站的证书又恰好出了问题,那么用户将在接下来的1年时间内都无法访问到你的网站,直到证书错误被修复,或者用户主动清除浏览器缓存。</strong></p><p>因此,建议在生产环境开启HSTS的时候,先将max-age的值设置小一些,例如5分钟,然后检查HSTS是否能正常工作,网站能否正常访问,之后再逐步将时间延长,例如1周、1个月,并在这个时间范围内继续检查HSTS是否正常工作,最后才改到1年</p><p><a href="">chrome://net-internals/#hsts</a></p><p><strong>如何把域名加入到 HSTS Preload List (这个操作要慎重)</strong></p><ul><li>具备一个有效的证书</li><li>在同一台主机上提供重定向响应,以及接收重定向过来的HTTPS请求</li><li>所有子域名均使用HTTPS</li><li>在根域名的HTTP响应头中,加入HSTS Header,并满足下列条件:</li><li>过期时间最短不得少于18周(10886400秒 )</li><li>必须包含includeSubDomains参数</li><li>必须包含preload参数</li><li>也就是说</li></ul><p><a href="https://source.chromium.org/chromium/chromium/src/+/master:net/http/transport_security_state_static.json">https://source.chromium.org/chromium/chromium/src/+/master:net/http/transport_security_state_static.json</a></p><p><a href="https://github.com/chromium/chromium">https://github.com/chromium/chromium</a></p><p>当你准好这些之后,可以在 HSTS Preload List的官网上(<a href="https://hstspreload.org)提交申请,或者了解更多详细的内容./">https://hstspreload.org)提交申请,或者了解更多详细的内容。</a></p>]]></content>
<summary type="html"><h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>有不少网站只通过HTTPS对外提供服务,但用户在访问某个网站的时候,在浏览器里却往往直接输入网站域名(例如 <a href="http://www.example.com/">www.example.com</a> ),甚至都不带www前缀,而不是输入完整的URL(例如 <a href="https://www.example.com/">https://www.example.com</a> )。不过浏览器依然能正确的使用HTTPS发起请求。这背后多亏了服务器和浏览器的协作。</p>
<p>现代浏览器,地址栏(多功能框)也可以用作搜索框。地址栏会自动使用 Google 进行搜索,但您也可将默认搜索引擎改设为另一搜索引擎。其实当我们在浏览器地址栏开始输入信息时,浏览器就已经开始进行工作了。</p></summary>
<category term="HTTPS" scheme="https://fengzhao.me/tags/https/"/>
</entry>
<entry>
<title>nginx-logrotation-日志切割</title>
<link href="https://fengzhao.me/2019/01/14/2019-01-14-nginx-logrotation/"/>
<id>https://fengzhao.me/2019/01/14/2019-01-14-nginx-logrotation/</id>
<published>2019-01-14T15:36:14.000Z</published>
<updated>2022-08-22T07:38:12.728Z</updated>
<content type="html"><![CDATA[<h1 id="nginx日志处理"><a href="#nginx日志处理" class="headerlink" title="nginx日志处理"></a>nginx日志处理</h1><p>在 nginx 中,主要有 access 日志、error 日志 、 rewrite 日志。前两种由 <code>ngx_http_log_module</code> 模块予以支持,<code>rewrite</code> 日志则由 <code>ngx_http_rewrite_module</code> 模块提供,这两个模块默认都已包含且启用。</p><span id="more"></span><p>日志文件包含了关于系统中发生的事件的有用信息,在排障过程中或者系统性能分析时经常被用到。</p><p>我们不管在生产环境还是开发环境,看日志是必不可少的,日志中往往包含很多有用的信息,有时候被<code>DDOS</code>、上传非法文件等等,我们都需要通过日志分析。</p><p>但是日志是跟访问量成正比的,你的访问量越大,你的各种级别日志就越多,日志文件大小会增长极快,服务器会很快消耗磁盘空间,这成个很严重的问题。</p><p>对于忙碌的服务器,日志文件大小会增长极快,服务器会很快消耗磁盘空间,这成了个问题。</p><p>除此之外,处理查看一个单个的庞大日志文件也常常是件十分棘手的事。你阅读、打开都要花费很大力气,那么怎么才能处理好这种情况?</p><p>logrotate 在很多 Linux 发行版上都是默认安装的。系统会定时运行 logrotate,一般是每天一次。系统是这么实现按天执行的。</p><p>crontab 会每天定时执行 /etc/cron.daily 目录下的脚本,而这个目录下有个文件叫 logrotate。</p><p>logrotate是个十分有用的工具,它可以自动对日志进行截断(或轮循)、压缩以及删除旧的日志文件。</p><p>例如,你可以设置logrotate,让/var/log/foo日志文件每30天轮循,并删除超过6个月的日志。配置完后,logrotate的运作完全自动化,不必进行任何进一步的人为干预。</p><p>系统会利用定时任务定时运行<code>logrotate</code>,一般是每天一次。<code>crontab</code>会每天定时执行<code>/etc/cron.daily</code>目录下的脚本,而这个目录下有个文件叫<code>logrotate</code>。我们只需要配置你所需要切割的参数就可以。</p><p>很多程序的会用到<code>logrotate</code>滚动日志,比如<code>nginx</code>。它们安装后,会在<code>/etc/logrotate.d</code>这个目录下增加自己的<code>logrotate</code>的配置文件。<code>logrotate</code>什么时候执行 <code>/etc/logrotate.d</code>下的配置呢?看到<code>/etc/logrotate.conf</code>里这行,一切就不言而喻了。</p><p>在 centos 上脚本内容是这样的:系统自带 crontab task:/etc/cron.daily/logrotate,每天运行一次。</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token punctuation">[</span>root@gop-sg-192-168-56-103 logrotate.d<span class="token punctuation">]</span><span class="token comment"># cat /etc/cron.daily/logrotate</span><span class="token comment">#!/bin/sh</span>/usr/sbin/logrotate <span class="token parameter variable">-s</span> /var/lib/logrotate/logrotate.status /etc/logrotate.conf<span class="token assign-left variable">EXITVALUE</span><span class="token operator">=</span><span class="token variable">$?</span><span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token variable">$EXITVALUE</span> <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span> /usr/bin/logger <span class="token parameter variable">-t</span> <span class="token function">logrotate</span> <span class="token string">"ALERT exited abnormally with [<span class="token variable">$EXITVALUE</span>]"</span><span class="token keyword">fi</span><span class="token builtin class-name">exit</span> <span class="token number">0</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>可以看到这个脚本主要做的事就是以<code>/etc/logrotate.conf</code>为配置文件执行了 logrotate。<strong>就是这样实现了每天执行一次 logrotate。</strong></p><p>因为我的系统执行 /etc/cron.daily 目录下的脚本不是我想滚动日志的时间,所以我把 /etc/cron.daily/logrotate 拷了出来,改了一下 logrotate 配置文件的路径,然后在 crontab 里加上一条指定时间执行这个脚本的记录,自定义周期滚动日志就大功告成了。这种自定义的方式有两点要注意:</p><ul><li><p>配置文件里一定要配置 rotate 文件数目 这个参数。如果不配置默认是 0 个,也就是只允许存在一份日志,刚切分出来的日志会马上被删除。多么痛的领悟,说多了都是泪。</p></li><li><p>执行 logrotate 命令最好加 -f 参数,不然有时候配置文件修改的内容不生效。</p></li></ul>]]></content>
<summary type="html"><h1 id="nginx日志处理"><a href="#nginx日志处理" class="headerlink" title="nginx日志处理"></a>nginx日志处理</h1><p>在 nginx 中,主要有 access 日志、error 日志 、 rewrite 日志。前两种由 <code>ngx_http_log_module</code> 模块予以支持,<code>rewrite</code> 日志则由 <code>ngx_http_rewrite_module</code> 模块提供,这两个模块默认都已包含且启用。</p></summary>
</entry>
<entry>
<title>Docker-compose简明教程</title>
<link href="https://fengzhao.me/2019/01/03/2019-01-03-Docker-compose%E7%AE%80%E6%98%8E%E6%95%99%E7%A8%8B/"/>
<id>https://fengzhao.me/2019/01/03/2019-01-03-Docker-compose%E7%AE%80%E6%98%8E%E6%95%99%E7%A8%8B/</id>
<published>2019-01-03T15:00:49.000Z</published>
<updated>2019-01-03T15:05:54.079Z</updated>
<content type="html"><![CDATA[<h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>docker-compose 是一个定义和运行多个 docker 容器实例的工具。它使用一个 docker-compose.yml(yaml格式)来定义一组关联的容器应用为一个项目(porject)。通过一个简单的命令,可以创建启动yml中配置的多个容器。它可以在所有的环境中使用,包括生产,开发,测试,持续集成环境。</p><span id="more"></span><p>docker-compose中有两个重要概念:</p><ul><li><p>服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。</p></li><li><p>项目(project):由一组关联的应用容器组成的一个完整业务单元。</p></li></ul><p>可以看出,一个项目可以由多个服务(容器)关联而成,Compose 面向项目进行管理。</p><p>docker-compose有很多命令,用于容器应用的整个生命周期:</p><ul><li>启动,停止,重建服务。</li><li>查看运行中服务的状态。</li><li>流式输出运行中服务的日志。</li><li>对服务运行的命令。</li></ul><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>一般在 Linux 中直接下载二进制文件即可:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">sudo</span> <span class="token function">curl</span> <span class="token parameter variable">-L</span> <span class="token string">"https://github.com/docker/compose/releases/download/1.22.0/docker-compose-<span class="token variable"><span class="token variable">$(</span><span class="token function">uname</span> <span class="token parameter variable">-s</span><span class="token variable">)</span></span>-<span class="token variable"><span class="token variable">$(</span><span class="token function">uname</span> <span class="token parameter variable">-m</span><span class="token variable">)</span></span>"</span> <span class="token parameter variable">-o</span> /usr/local/bin/docker-compose$ <span class="token function">sudo</span> <span class="token function">chmod</span> +x /usr/local/bin/docker-compose$ <span class="token function">docker-compose</span> <span class="token parameter variable">--version</span><span class="token function">docker-compose</span> version <span class="token number">1.22</span>.0, build f46880fe<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="开始使用docker-compose"><a href="#开始使用docker-compose" class="headerlink" title="开始使用docker-compose"></a>开始使用docker-compose</h3><pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token comment">#创建文件夹 dockertest ,在该目录中编写app.py,内容为</span><span class="token comment">#!/usr/bin/env python</span><span class="token comment"># -*- coding: utf-8 -*-</span><span class="token keyword">import</span> time<span class="token keyword">import</span> redis<span class="token keyword">from</span> flask <span class="token keyword">import</span> Flaskapp <span class="token operator">=</span> Flask<span class="token punctuation">(</span>__name__<span class="token punctuation">)</span>cache <span class="token operator">=</span> redis<span class="token punctuation">.</span>Redis<span class="token punctuation">(</span>host<span class="token operator">=</span><span class="token string">'redis'</span><span class="token punctuation">,</span> port<span class="token operator">=</span><span class="token number">6379</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">get_hit_count</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> retries <span class="token operator">=</span> <span class="token number">5</span> <span class="token keyword">while</span> <span class="token boolean">True</span><span class="token punctuation">:</span> <span class="token keyword">try</span><span class="token punctuation">:</span> <span class="token keyword">return</span> cache<span class="token punctuation">.</span>incr<span class="token punctuation">(</span><span class="token string">'hits'</span><span class="token punctuation">)</span> <span class="token keyword">except</span> redis<span class="token punctuation">.</span>exceptions<span class="token punctuation">.</span>ConnectionError <span class="token keyword">as</span> exc<span class="token punctuation">:</span> <span class="token keyword">if</span> retries <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">:</span> <span class="token keyword">raise</span> exc retries <span class="token operator">-=</span> <span class="token number">1</span> time<span class="token punctuation">.</span>sleep<span class="token punctuation">(</span><span class="token number">0.5</span><span class="token punctuation">)</span><span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>route</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span><span class="token keyword">def</span> <span class="token function">hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> count <span class="token operator">=</span> get_hit_count<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token string">'Hello World! I have been seen {} times.\n'</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>count<span class="token punctuation">)</span><span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">"__main__"</span><span class="token punctuation">:</span> app<span class="token punctuation">.</span>run<span class="token punctuation">(</span>host<span class="token operator">=</span><span class="token string">"0.0.0.0"</span><span class="token punctuation">,</span> debug<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span> <span class="token comment">#编写 Dockerfile 文件,内容为</span>FROM python<span class="token punctuation">:</span><span class="token number">3.6</span><span class="token operator">-</span>alpineADD <span class="token punctuation">.</span> <span class="token operator">/</span>codeWORKDIR <span class="token operator">/</span>codeRUN pip install redis flaskCMD <span class="token punctuation">[</span><span class="token string">"python"</span><span class="token punctuation">,</span> <span class="token string">"app.py"</span><span class="token punctuation">]</span><span class="token comment">#编写 docker-compose.yml 文件,内容为</span>version<span class="token punctuation">:</span> <span class="token string">'3'</span>services<span class="token punctuation">:</span> web<span class="token punctuation">:</span> build<span class="token punctuation">:</span> <span class="token punctuation">.</span> ports<span class="token punctuation">:</span> <span class="token operator">-</span> <span class="token string">"5000:5000"</span> redis<span class="token punctuation">:</span> image<span class="token punctuation">:</span> <span class="token string">"redis:alpine"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>docker-compose.yml 文件中定义两个服务:web 和redis。</p><p>web:使用了Dockerfile中构建的镜像,把容器中的5000端口映射到宿主机的8300端口。</p><p>redis:使用了从 Docker Hub registry 中拉取的官方 redis 镜像。</p><h3 id="使用说明"><a href="#使用说明" class="headerlink" title="使用说明"></a>使用说明</h3><p>docker-compose的基本使用格式如下:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">Usage: <span class="token function">docker-compose</span> <span class="token punctuation">[</span>-f <span class="token operator"><</span>arg<span class="token operator">></span><span class="token punctuation">..</span>.<span class="token punctuation">]</span> <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token punctuation">[</span>COMMAND<span class="token punctuation">]</span> <span class="token punctuation">[</span>ARGS<span class="token punctuation">..</span>.<span class="token punctuation">]</span> <span class="token function">docker-compose</span> <span class="token parameter variable">--help</span> <span class="token function">docker-compose</span> <span class="token builtin class-name">command</span> <span class="token parameter variable">--help</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h4 id="选项说明"><a href="#选项说明" class="headerlink" title="选项说明"></a>选项说明</h4><ul><li><code>-f FILE</code> 指定使用的 compose 模板文件(显示指定文件路径),默认为当前目录下的 docker-compose.yml ,如果没有则报错。</li><li><code>-p NAME</code> 指定项目名称,默认将使用所在目录名称作为项目名。</li><li><code>--verbose</code> 显示详细输出。</li><li><code>--log-level LEVEL</code> 设置日志级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL)。</li><li><code>-v, --version</code> 打印版本并退出。</li></ul><h4 id="命令说明"><a href="#命令说明" class="headerlink" title="命令说明"></a>命令说明</h4><h5 id="build"><a href="#build" class="headerlink" title="build"></a>build</h5><p>构建或重建项目中的服务容器。</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">Usage: build <span class="token punctuation">[</span>options<span class="token punctuation">]</span> <span class="token punctuation">[</span>--build-arg <span class="token assign-left variable">key</span><span class="token operator">=</span>val<span class="token punctuation">..</span>.<span class="token punctuation">]</span> <span class="token punctuation">[</span>SERVICE<span class="token punctuation">..</span>.<span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>服务容器一旦构建后,将会带上一个标记名,例如对于 web 项目中的一个 db 容器,可能是 web_db。<br>可以随时在项目目录下运行 <code>docker-compose build</code> 来重新构建服务。</p><p>选项包括:</p><ul><li><code>--force-rm</code> 删除构建过程中的临时容器。</li><li><code>--no-cache</code> 构建镜像过程中不使用 cache(这将加长构建过程)。</li><li><code>--pull</code> 始终尝试通过 pull 来获取更新版本的镜像。</li><li><code>-m, --memory MEM </code> 为创建的容器设置内存限制</li></ul>]]></content>
<summary type="html"><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>docker-compose 是一个定义和运行多个 docker 容器实例的工具。它使用一个 docker-compose.yml(yaml格式)来定义一组关联的容器应用为一个项目(porject)。通过一个简单的命令,可以创建启动yml中配置的多个容器。它可以在所有的环境中使用,包括生产,开发,测试,持续集成环境。</p></summary>
<category term="docker" scheme="https://fengzhao.me/tags/docker/"/>
</entry>
<entry>
<title>docker入门概述</title>
<link href="https://fengzhao.me/2019/01/03/2019-01-03-docker%E5%85%A5%E9%97%A8%E6%A6%82%E8%BF%B0/"/>
<id>https://fengzhao.me/2019/01/03/2019-01-03-docker%E5%85%A5%E9%97%A8%E6%A6%82%E8%BF%B0/</id>
<published>2019-01-03T14:48:46.000Z</published>
<updated>2019-01-13T16:21:22.757Z</updated>
<content type="html"><![CDATA[<h2 id="Docker-概述"><a href="#Docker-概述" class="headerlink" title="Docker 概述"></a>Docker 概述</h2><p>Docker是一个开发,运输和运行应用程序的开放平台。 Docker使您可以将应用程序与基础架构分离,以便快速交付软件。使用Docker,您可以像管理应用程序一样管理基础架构。通过利用Docker的方法快速发送,测试和部署代码,您可以显着减少编写代码和在生产中运行代码之间的延迟。</p><p>Docker提供了在称为容器的松散隔离环境中打包和运行应用程序的功能。隔离和安全性允许您在给定主机上同时运行多个容器。容器是轻量级的,因为它们不需要管理程序的额外负载,而是直接在主机内核中运行。这意味着您可以在给定硬件组合上运行比使用虚拟机时更多的容器。您甚至可以在实际虚拟机的主机中运行Docker容器!</p><span id="more"></span><p>开发者可以根据配置文件将应用及依赖包放到一个可移植的容器中,然后发布到一定版本以上的任何流行的操作系统上,实现轻量级别的虚拟化。容器完全使用沙箱机制,通过镜像来保证运行环境的一致性,启动速度秒级之内,可以更好的满足云计算的自动化以及弹性扩容等场景。</p><p>Docker 可以在容器内部快速自动化的部署应用,并通过操作系统内核技术( namespaces 、cgroups 等)为容器提供资源隔离与安全保障。<br>Docker 是以 Docker 容器为资源分割和调度的基本单位,封装整个软件运行时环境为开发者和系统管理员设计的,用于构建、发布和运行分布式应用的平台。Docker 是一个跨平台、可移植并且简单易用的容器解决方案。</p><p>关于 Docker 中一些 更详细的描述和定义,可以参考下面这几篇网站:</p><blockquote><p><a href="https://www.docker.com/">https://www.docker.com/</a></p><p><a href="http://guide.daocloud.io/dcs/docker-9153160.html">http://guide.daocloud.io/dcs/docker-9153160.html</a></p><p><a href="https://www.163yun.com/help/documents/158369209000316928">https://www.163yun.com/help/documents/158369209000316928</a></p></blockquote><h3 id="Docker-引擎"><a href="#Docker-引擎" class="headerlink" title="Docker 引擎"></a>Docker 引擎</h3><p>Docker Engine是一个客户端 - 服务器应用程序,包含以下主要组件:</p><ul><li>服务端,是一种长时间运行的程序(守护进程),称为 docker daemon(dockerd命令)。</li><li>REST API 接口,它指定程序可以用来与守护进程通信并指示它做什么的接口。</li><li>客户端命令行( command line interface)(docker命令)。</li></ul><p><img src="/engine-components-flow.png" alt="engine-components-flow"></p><p>docker 客户端命令或 REST API 可以与服务端通讯,向服务端的守护进程下达指令。</p><p>docker daemon 创建和管理Docker对象,例如镜像,容器,网络和数据卷等。</p><h3 id="Docker架构"><a href="#Docker架构" class="headerlink" title="Docker架构"></a>Docker架构</h3><p>Docker 使用的是 c/s 架构,Docker 客户端与 Docker 守护进程通讯,后者负责构建,运行,分发 Docker 容器。Docker 客户端和守护进程可以在同一台机器,也可以用 Docker 客户端连接远端 docker 守护进程。Docker自带的客户端程序是通过 Unix socket 套接字文件来与服务端通讯,Docker 官方也提供了 REST 风格的 API,你也可以开发自己的客户端来使用 HTTP 协议来与服务端通讯。</p><p><img src="/docker-architecture.png" alt="docker-architecture"></p><center>docker架构图</center><h4 id="docker-守护进程"><a href="#docker-守护进程" class="headerlink" title="docker 守护进程"></a>docker 守护进程</h4><p>Docker守护程序(<code>dockerd</code>)监听 Docker API 请求并管理 Docker 对象,如图像,容器,网络和卷。守护程序还可以与其他守护程序通信以管理 Docker 服务。</p><h4 id="docker-客户端"><a href="#docker-客户端" class="headerlink" title="docker 客户端"></a>docker 客户端</h4><p>Docker客户端(<code>docker</code>)是许多 Docker 用户与 Docker 交互的主要方式。当您使用诸如docker run之类的命令时,客户端会将这些命令发送到 <code>dockerd</code> ,后者将其执行。 <code>docker</code> 命令使用 Docker API 。 Docker 客户端可以与多个守护进程通信。</p><h4 id="Docker-Registry"><a href="#Docker-Registry" class="headerlink" title="Docker Registry"></a>Docker Registry</h4><p>Docker Registry 就是一个镜像商店,它里面可以包括各种镜像,可以分为私有仓库和公有仓库(其中 docker hub 最为出名,它是由 docker 公司开发,国内有阿里云等镜像市场)。我们常用的各种开源软件和运行时环境,基本上都可以在 registry 上找到 docker 镜像。</p><p>一个 Docker Registry 中可以包含多个仓库(<code>Repository</code>);每个仓库可以包含多个标签(<code>Tag</code>);每个标签对应一个镜像。 </p><blockquote><p>注意:docker registry是镜像站点,仓库是镜像商店内的软件,人们常说的搭建私有仓库,应该理解成搭建私有docker registry。这与 maven 或者其他私有代码仓库的概念有些区别。</p></blockquote><p>通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <code><仓库名>:<标签></code> 的格式来指定具体是哪个软件哪个版本的镜像。如果不给出标签,将以 <code>latest</code> 作为默认标签。</p><p>以 <a href="https://store.docker.com/images/ubuntu">Ubuntu 镜像</a> 为例,<code>ubuntu</code> 是仓库的名字,其内包含有不同的版本标签,如,<code>14.04</code>, <code>16.04</code>。我们可以通过 <code>ubuntu:14.04</code>,或者 <code>ubuntu:16.04</code> 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 <code>ubuntu</code>,那将视为 <code>ubuntu:latest</code>。</p><h4 id="Docker-对象"><a href="#Docker-对象" class="headerlink" title="Docker 对象"></a>Docker 对象</h4><p>使用 Docker 时,将会创建和使用镜像,容器,网络,数据卷,插件和其他对象。这些介绍其中一些对象。</p><p>镜像:镜像是一个轻量级,独立的,可执行的软件包,它包括运行这个软件的一切:代码,运行时,系统等。</p><p>容器:容器就是运行启动起来的镜像。同一个镜像可以启动多个,可以简单理解为容器就是镜像的实例化。</p><p>关于容器和镜像的基本概念,可以参考<a href="http://dockone.io/article/6051">这篇文章</a>,我认为这篇文章名副其实,把 docker 的基本概念介绍的非常清楚。</p><h3 id="安装-docker"><a href="#安装-docker" class="headerlink" title="安装 docker"></a>安装 docker</h3><p>docker的安装见官网安装教程:</p><blockquote><p><a href="https://docs.docker.com/install/">https://docs.docker.com/install/</a></p></blockquote><h4 id="第一个-docker-实例"><a href="#第一个-docker-实例" class="headerlink" title="第一个 docker 实例"></a>第一个 docker 实例</h4><p>看完一大堆理论,赶紧去运行你的第一个 docker 容器吧。docker的使用非常方便。 </p><p>一句命令就可以启动 一个 nginx ,感受一下 docker 的方便吧:</p><p>docker run -d -p 8080:80 nginx </p><p>其中 -d 指后台运行,-p 将容器内的 80 端口映射到宿主机的 8080 端口上。</p><p>启动完访问宿主机的 8080 端口,就能见到熟悉的 nginx 欢迎界面了。</p><h3 id="配置-docker"><a href="#配置-docker" class="headerlink" title="配置 docker"></a>配置 docker</h3><h4 id="运行-docker"><a href="#运行-docker" class="headerlink" title="运行 docker"></a>运行 docker</h4><p>安装好 docker 之后,一般 docker 守护进程会自动启动,我们可以通过直接启动或系统服务的方式来启动 docker。</p><h5 id="直接启动"><a href="#直接启动" class="headerlink" title="直接启动"></a>直接启动</h5><p>直接执行 dockerd 命令就可以启动守护进程,它会在前台运行,输出启动日志到终端,使用 ctr+c 命令来停止进程。可以用这种方式来进行测试。</p><h5 id="开机启动"><a href="#开机启动" class="headerlink" title="开机启动"></a>开机启动</h5><p>大多数当前的Linux发行版(RHEL,CentOS,Fedora,Ubuntu 16.04 及更高版本)使用 systemd 工具来管理系统启动时启动的服务。 </p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ systemctl <span class="token builtin class-name">enable</span> <span class="token function">docker</span> <span class="token comment">#开机自启</span>$ systemctl disable <span class="token function">docker</span> <span class="token comment">#开机自启</span>$ systemctl start <span class="token function">docker</span> <span class="token comment">#启动docker</span>$ systemctl restart <span class="token function">docker</span> <span class="token comment">#重启</span>$ systemctl stop <span class="token function">docker</span> <span class="token comment">#重启</span>$ systemctl status <span class="token function">docker</span> <span class="token comment">#查看状态</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="服务端配置"><a href="#服务端配置" class="headerlink" title="服务端配置"></a>服务端配置</h4><p>docker 守护进程的配置,有两种方式指定:</p><ul><li>通过在 dockerd 命令后面指定启动参数。</li><li>通过 dockerd –config-file 来指定一个json 格式的配置文件 (默认在/etc/docker/daemon.json)</li></ul><p>默认地,这个配置文件不存在,系统按照默认配置启动 docker ,如果想自定义,可以创建这个文件。下面是一个简单的示例:</p><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"debug"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token property">"tls"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token property">"tlscert"</span><span class="token operator">:</span> <span class="token string">"/var/docker/server.pem"</span><span class="token punctuation">,</span> <span class="token property">"tlskey"</span><span class="token operator">:</span> <span class="token string">"/var/docker/serverkey.pem"</span><span class="token punctuation">,</span> <span class="token property">"hosts"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"tcp://192.168.59.3:2376"</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这个配置文件指定以调试模式启动,开启 TLS 安全传输协议,证书和密钥路径,并监听到 192.168.59.3:2376 。这与下面这个命令是一样的。</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">dockerd <span class="token parameter variable">--debug</span> <span class="token punctuation">\</span> <span class="token parameter variable">--tls</span><span class="token operator">=</span>true <span class="token punctuation">\</span> <span class="token parameter variable">--tlscert</span><span class="token operator">=</span>/var/docker/server.pem <span class="token punctuation">\</span> <span class="token parameter variable">--tlskey</span><span class="token operator">=</span>/var/docker/serverkey.pem <span class="token punctuation">\</span> <span class="token parameter variable">--host</span> tcp://192.168.59.3:2376<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>具体的配置选项可以参考 <a href="https://docs.docker.com/engine/reference/commandline/dockerd/">dockerd reference doc</a> 或者使用 dockerd –help来查看。</p><h4 id="远程访问"><a href="#远程访问" class="headerlink" title="远程访问"></a>远程访问</h4><p>docker 守护进程使用 unix, tcp, fd 三种类型的 Socket 通信来监听 <a href="https://docs.docker.com/develop/sdk/">Docker Engine API</a> 。</p><p>默认地,docker 会创建一个 /var/run/docker.sock 文件,它只允许本地的 root 用户来连接,它需要 root 权限,或 docker 用户组。通常我们不建议开启远程访问。</p><p>默认地,docker 没有开启远程访问,如果需要开启远程访问,需要开启 tcp socket 通讯,需要注意的是,默认安装没有启用对服务端访问的加密和认证。也就是说一旦开启远程访问和服务器外网,任何人都可以通过 docker 客户端来访问并控制你的 docker 守护进程来进行创建删除容器等操作。所以必须要开启加密认证或者在守护进程前面加上一个安全的代理。</p><blockquote><p><font color=#FF0000 >注意:不要轻易开放远程访问,如果开放,一定要确认开放对象是可信赖的或者开启访问认证和加密传输。</font></p></blockquote><p>譬如,docker 服务器的内网 ip 是 10.0.0.1,外网 ip 是 45.57.36.48 ,使用 -H tcp://0.0.0.0:2375 来监听的 2375 端口,使用 -H tcp://45.57.36.48:2376 来监听 2376 端口,可以很方便的实现 2375 端口用于非加密访问,2376端口用于加密访问,然后通过防火墙规则限定 2375 端口对指定管理终端开放。</p><p>下面是一个简单的远程访问的例子:</p><p>两台服务器,都安装好 docker ,192.168.1.2 作为服务端开启远程访问。</p><p>服务端配置文件:</p><pre class="line-numbers language-none"><code class="language-none">{ "hosts": ["tcp://0.0.0.0:2375 ","unix:///var/run/docker.sock","-H fd:// "]}<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>重新加载服务端配置文件</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ systemctl restart docker.service<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>在客户端,有两种方式访问服务端的 docker 服务:</p><ul><li><p>通过 <a href="https://docs.docker.com/engine/api/v1.39/">docker engine web api</a> 连接 server ,访问服务端的 info 接口。</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> http://192.168.1.2:2375/info <span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></li><li><p>通过 docker 客户端命令访问服务端:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> <span class="token parameter variable">-H</span> tcp://192.168.1.2:2375 info<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></li></ul><p>默认地,在客户端执行 docker 命令是连接本地的守护进程,可以修改 DOCKER_HOST 环境变量来改变默认连接:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">export</span> <span class="token assign-left variable">DOCKER_HOST</span><span class="token operator">=</span><span class="token string">"tcp://192.168.1.2:2375"</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>通过将 DOCKER_HOST 置空来恢复本地连接:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">export</span> <span class="token assign-left variable">DOCKER_HOST</span><span class="token operator">=</span><span class="token string">""</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="docker-命令"><a href="#docker-命令" class="headerlink" title="docker 命令"></a>docker 命令</h4><p>docker 命令主要是用来向服务端守护进程发送控制指令,来进行构建镜像,启动容器等一些列操作。它包括一系列子命令。每个子命令都有其单独的选项,查看 docker –help 来看命令概述,通过 docker COMMAND –help 来看子命令详细用法。</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">root@fengzhao-linux-server:~<span class="token comment"># docker --help</span>Usage: <span class="token function">docker</span> <span class="token punctuation">[</span>OPTIONS<span class="token punctuation">]</span> COMMANDA self-sufficient runtime <span class="token keyword">for</span> containersOptions: <span class="token parameter variable">--config</span> string Location of client config files <span class="token punctuation">(</span>default <span class="token string">"/root/.docker"</span><span class="token punctuation">)</span> -D, <span class="token parameter variable">--debug</span> Enable debug mode -H, <span class="token parameter variable">--host</span> list Daemon socket<span class="token punctuation">(</span>s<span class="token punctuation">)</span> to connect to -l, --log-level string Set the logging level <span class="token punctuation">(</span><span class="token string">"debug"</span><span class="token operator">|</span><span class="token string">"info"</span><span class="token operator">|</span><span class="token string">"warn"</span><span class="token operator">|</span><span class="token string">"error"</span><span class="token operator">|</span><span class="token string">"fatal"</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>default <span class="token string">"info"</span><span class="token punctuation">)</span> <span class="token parameter variable">--tls</span> Use TLS<span class="token punctuation">;</span> implied by <span class="token parameter variable">--tlsverify</span> <span class="token parameter variable">--tlscacert</span> string Trust certs signed only by this CA <span class="token punctuation">(</span>default <span class="token string">"/root/.docker/ca.pem"</span><span class="token punctuation">)</span> <span class="token parameter variable">--tlscert</span> string Path to TLS certificate <span class="token function">file</span> <span class="token punctuation">(</span>default <span class="token string">"/root/.docker/cert.pem"</span><span class="token punctuation">)</span> <span class="token parameter variable">--tlskey</span> string Path to TLS key <span class="token function">file</span> <span class="token punctuation">(</span>default <span class="token string">"/root/.docker/key.pem"</span><span class="token punctuation">)</span> <span class="token parameter variable">--tlsverify</span> Use TLS and verify the remote -v, <span class="token parameter variable">--version</span> Print version information and quitManagement Commands: config Manage Docker configs container Manage containers image Manage images network Manage networks <span class="token function">node</span> Manage Swarm nodes plugin Manage plugins secret Manage Docker secrets <span class="token function">service</span> Manage services stack Manage Docker stacks swarm Manage Swarm system Manage Docker trust Manage trust on Docker images volume Manage volumesCommands: attach Attach <span class="token builtin class-name">local</span> standard input, output, and error streams to a running container build Build an image from a Dockerfile commit Create a new image from a container<span class="token string">'s changes cp Copy files/folders between a container and the local filesystem create Create a new container diff Inspect changes to files or directories on a container'</span>s filesystem events Get real <span class="token function">time</span> events from the server <span class="token builtin class-name">exec</span> Run a <span class="token builtin class-name">command</span> <span class="token keyword">in</span> a running container <span class="token builtin class-name">export</span> Export a container<span class="token string">'s filesystem as a tar archive history Show the history of an image images List images import Import the contents from a tarball to create a filesystem image info Display system-wide information inspect Return low-level information on Docker objects kill Kill one or more running containers load Load an image from a tar archive or STDIN login Log in to a Docker registry logout Log out from a Docker registry logs Fetch the logs of a container pause Pause all processes within one or more containers port List port mappings or a specific mapping for the container ps List containers pull Pull an image or a repository from a registry push Push an image or a repository to a registry rename Rename a container restart Restart one or more containers rm Remove one or more containers rmi Remove one or more images run Run a command in a new container save Save one or more images to a tar archive (streamed to STDOUT by default) search Search the Docker Hub for images start Start one or more stopped containers stats Display a live stream of container(s) resource usage statistics stop Stop one or more running containers tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE top Display the running processes of a container unpause Unpause all processes within one or more containers update Update configuration of one or more containers version Show the Docker version information wait Block until one or more containers stop, then print their exit codesRun '</span><span class="token function">docker</span> COMMAND --help' <span class="token keyword">for</span> <span class="token function">more</span> information on a command.root@fengzhao-linux-server:~<span class="token comment">#</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>下面是一些常见的 docker 命令:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> <span class="token parameter variable">--help</span> <span class="token comment">#查看帮助</span>$ <span class="token function">docker</span> version <span class="token comment">#查看版本</span>$ <span class="token function">docker</span> pull image <span class="token comment">#下载镜像</span>$ <span class="token function">docker</span> image <span class="token function">ls</span> <span class="token comment"># 列出所有镜像</span>$ <span class="token function">docker</span> run image <span class="token comment">#从镜像启动一个新的容器</span>$ <span class="token function">docker</span> <span class="token function">ps</span> <span class="token comment">#查看运行中的容器,-a 查看所有容器</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="Docker-对象-1"><a href="#Docker-对象-1" class="headerlink" title="Docker 对象"></a>Docker 对象</h2><h3 id="镜像"><a href="#镜像" class="headerlink" title="镜像"></a>镜像</h3><p>docker 镜像的概念,前面已经大致讲过,这里不再赘述,镜像的构建,一般会基于某个父镜像去构建。镜像的构建方法一般有三种方式:</p><ul><li>通过一个 Dockerfile 文件来描述镜像中的内容和操作,然后用 docker build 命令构建镜像。</li><li>启动一个容器后,在容器中通过一些基本操作做出改变后,用 docker commit 将容器提交为镜像。</li><li>按上述之一方式做好镜像后,推送到镜像仓库,下次使用时,可以直接从镜像仓库拉取到本地。</li></ul><p>举个例子,我们在一台新电脑上安装操作系统时,主要步骤是去微软官网下载 windows iso 镜像,然后刻录到U盘,然后去电脑上安装,然后自己去安装各种开发环境,和常用软件。我们可以在安装好软件后,通过工具创建镜像,这样下次通过自己制作的镜像安装操作系统,就会自带这些额外的软件,这就是第二种方式。但是有人认为制作镜像还是要手工安装软件比较麻烦。于是写了一个文件,里面包含安装开发环境和常用软件的指令,执行这个文件就会自动创建自己制作的镜像,这就是第一种方式,这个文件就是 Dockerfile。</p><p>通常,使用 Dockerfile 文件来构建镜像是比较多的做法。Dockerfile 中有一系列指令来构建镜像。</p><p>docker 镜像的命名空间主要是 Registry/Users/Repository/Tag,分别表示 Registry地址/用户空间/仓库名称/标签。</p><p>默认地的 Registry 是 dockerhub ,如果通过 docker image ls 查看到某个镜像没有 Registry ,那就是来自docker hub。</p><p>一些大型软件 在docker hub 上的镜像,都是由官方(docker 官方或软件发行官方)维护,在 <a href="https://hub.docker.com/">docker hub</a>上搜索可以看到 official 字样,这类镜像,一般没有用户名称,或者其名称为 library 。</p><p>主要分为以下几种情况</p><ol><li>docker hub 上的官方镜像为默认Registry ubuntu:16.04</li><li>docker hub 上用户空间下的镜像 fengzhao/nginx:latest</li><li>私有 docker registry上的镜像 hub.mycompany.com/dev/nginx:latest</li></ol><p>列出所有镜像:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">root@fengzhao-linux-server:~<span class="token comment"># docker image ls</span>root@fengzhao-linux-server:~<span class="token comment"># docker image ls</span>REPOSITORY TAG IMAGE ID CREATED SIZEnginx latest 62f816a209e6 <span class="token number">2</span> months ago 109MBhub.demo.com/fengzhao/nginx latest 62f816a209e6 <span class="token number">2</span> months ago 109MBchinafengzhao/nginx v1.0 62f816a209e6 <span class="token number">2</span> months ago 109MBroot@fengzhao-linux-server:~<span class="token comment">#</span>root@fengzhao-linux-server:~<span class="token comment">#</span><span class="token comment"># 这些,其实都是同一个镜像,只是不同的引用而已,因为他们</span><span class="token comment"># 第一个是官方的nginx镜像,</span><span class="token comment"># 第二个是给官方镜像打了新标签,可以通过 docker pull 推到自己本地的 docker Registry 中。</span><span class="token comment"># 第三个是给官方镜像打了用户空间(并带版本号),可以推到 dockerhub 中的 chinafengzhao 用户空间下。</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>每个镜像,下载到当前服务器内,都有一个镜像 id来唯一标识这个镜像,我们可以给同一个镜像打多个标签,用来做镜像版本管理,使用 docker tag 命令来给镜像打标签:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> tag SOURCE_IMAGE<span class="token punctuation">[</span>:TAG<span class="token punctuation">]</span> TARGET_IMAGE<span class="token punctuation">[</span>:TAG<span class="token punctuation">]</span> <span class="token comment">#给镜像打一个标签 </span>$ <span class="token function">docker</span> image <span class="token function">rm</span> Registry/Users/Repository/Tag <span class="token comment">#移除某个标签,当最后一个时</span><span class="token comment"># 当某个镜像的Registry/Users/Repository/Tag均为空时,无法引用这个镜像,成为虚悬镜像。</span><span class="token comment"># 产生虚悬镜像的原因,从 Registry 拉取3.2版本的镜像,然后 Registry 重新制作3.2版本的镜像,然后你再#拉这个镜像,这样最开始的那个3.2的镜像的REPOSITORY和TAG都被置空,无法引用</span>$ <span class="token function">docker</span> prune <span class="token comment"># 删除虚悬镜像</span>$ <span class="token function">docker</span> image <span class="token function">ls</span> <span class="token parameter variable">-f</span> <span class="token assign-left variable">dangling</span><span class="token operator">=</span>true <span class="token comment"># 列出所有虚悬镜像</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="镜像管理常用命令"><a href="#镜像管理常用命令" class="headerlink" title="镜像管理常用命令"></a>镜像管理常用命令</h4><p>docker image COMMAND 是镜像管理的基本命令,可以通过帮助命令,逐层查看其所有的子命令:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">root@fengzhao-linux-server:~<span class="token comment"># docker image --help</span>Usage: <span class="token function">docker</span> image COMMANDManage imagesCommands: build Build an image from a Dockerfile <span class="token function">history</span> Show the <span class="token function">history</span> of an image <span class="token function">import</span> Import the contents from a tarball to create a filesystem image inspect Display detailed information on one or <span class="token function">more</span> images load Load an image from a <span class="token function">tar</span> archive or STDIN <span class="token function">ls</span> List images prune Remove unused images pull Pull an image or a repository from a registry push Push an image or a repository to a registry <span class="token function">rm</span> Remove one or <span class="token function">more</span> images save Save one or <span class="token function">more</span> images to a <span class="token function">tar</span> archive <span class="token punctuation">(</span>streamed to STDOUT by default<span class="token punctuation">)</span> tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGERun <span class="token string">'docker image COMMAND --help'</span> <span class="token keyword">for</span> <span class="token function">more</span> information on a command.root@fengzhao-linux-server:~<span class="token comment">#</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>下面仅列举一些常用的 docker 镜像管理命令:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token function">docker</span> pull Registry/Users/Repository/Tag <span class="token comment"># 从registry上拉取镜像,私有的可能需要docker login认证</span>$ <span class="token function">docker</span> image <span class="token function">ls</span> <span class="token comment"># 列出所有镜像</span>$ <span class="token function">docker</span> rmi 45fb1e3aa <span class="token comment"># 删除这个id的镜像,参数可以是id,也可以是repoistry+tag,</span>$ <span class="token function">docker</span> tag busybox:latest fengzhao/busybox:latest <span class="token comment"># 给镜像添加额外的标签</span>$ <span class="token function">docker</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h4 id="Dockerfile-构建镜像"><a href="#Dockerfile-构建镜像" class="headerlink" title="Dockerfile 构建镜像"></a>Dockerfile 构建镜像</h4><p>Dockerfile 是由一系列命令和参数构成的脚本文件,一个 Dockerfile 里面包含了构建整个 image 的完整命令。Docker 通过 docker build 执行 Dockerfile 中的一系列命令自动构建 image 。</p><p>通过 docker build 命令来从 Dockerfile 和下文中构建镜像,上下文一般就是 Dockerfile 文件所在的路径, 其中包含一系列制作镜像的所需的原文件,上下文可以在某个路径,或者是某个 URL (一般是git repo,不建议用 URL)中。上下文会被递归处理,所以路径下可以包含子目录。</p><p>Dockerfile 包含一系列指令,它必须以 FROM 作为第一行,表示基于某个父镜像构建。</p><p>构建过程是 docker daemon 来执行的,第一件事就是把整个上下文传给 daemon 。在多数情况下,创建一个空文件夹来存放 Dockerfile 和构建镜像所需的文件。把这个文件夹作为上下文。可以在任何位置执行 docker build 构建镜像,通过 -f 选项来指定 Dockerfile 文件。</p><blockquote><p><code>警告</code>:不要使用根目录<code>/</code>作为上下文,因为它会导致构建将硬盘中的所有内容传输到Docker守护进程。</p></blockquote><p>下面是一个 nginx 的 Dockerfile 文件:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># /data/docker/nginx/Dockerfile</span>FROM ubuntu:12.04MAINTAINER fengzhao <span class="token operator"><</span>[email protected]<span class="token operator">></span>RUN <span class="token function">apt-get</span> updateRUN <span class="token function">apt-get</span> <span class="token parameter variable">-y</span> <span class="token function">install</span> nginx<span class="token comment"># put my local site to /var/www</span>ADD index.html /var/www/html/<span class="token comment"># expose httpd port</span>EXPOSE <span class="token number">80</span><span class="token comment"># the command to run</span>CMD <span class="token punctuation">[</span><span class="token string">"/usr/sbin/nginx"</span><span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>通过 docker build 命令来构建镜像:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ <span class="token builtin class-name">cd</span> /data/docker/nginx/$ <span class="token function">docker</span> build <span class="token parameter variable">-t</span> chinafengzhao/nginx:1.0.2 <span class="token parameter variable">-t</span> shykes/myapp:latest <span class="token builtin class-name">.</span> <span class="token comment"># . 表示 Dockerfile 文件所在目录,当前构建的上下文。也可以用 -f /path/Dockerfile 来指定构建路径</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>关于 docker build 命令的详细用法,可以查看帮助:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">root@fengzhao-linux-server:~<span class="token comment">#</span>root@fengzhao-linux-server:~<span class="token comment">#</span>root@fengzhao-linux-server:~<span class="token comment"># docker build --help</span>Usage: <span class="token function">docker</span> build <span class="token punctuation">[</span>OPTIONS<span class="token punctuation">]</span> <span class="token environment constant">PATH</span> <span class="token operator">|</span> URL <span class="token operator">|</span> -Build an image from a DockerfileOptions: --add-host list Add a custom host-to-IP mapping <span class="token punctuation">(</span>host:ip<span class="token punctuation">)</span> --build-arg list Set build-time variables --cache-from strings Images to consider as cache sources --cgroup-parent string Optional parent cgroup <span class="token keyword">for</span> the container <span class="token parameter variable">--compress</span> Compress the build context using <span class="token function">gzip</span> --cpu-period int Limit the CPU CFS <span class="token punctuation">(</span>Completely Fair Scheduler<span class="token punctuation">)</span> period --cpu-quota int Limit the CPU CFS <span class="token punctuation">(</span>Completely Fair Scheduler<span class="token punctuation">)</span> <span class="token function">quota</span> -c, --cpu-shares int CPU shares <span class="token punctuation">(</span>relative weight<span class="token punctuation">)</span> --cpuset-cpus string CPUs <span class="token keyword">in</span> <span class="token function">which</span> to allow execution <span class="token punctuation">(</span><span class="token number">0</span>-3, <span class="token number">0,1</span><span class="token punctuation">)</span> --cpuset-mems string MEMs <span class="token keyword">in</span> <span class="token function">which</span> to allow execution <span class="token punctuation">(</span><span class="token number">0</span>-3, <span class="token number">0,1</span><span class="token punctuation">)</span> --disable-content-trust Skip image verification <span class="token punctuation">(</span>default <span class="token boolean">true</span><span class="token punctuation">)</span> -f, <span class="token parameter variable">--file</span> string Name of the Dockerfile <span class="token punctuation">(</span>Default is <span class="token string">'PATH/Dockerfile'</span><span class="token punctuation">)</span> --force-rm Always remove intermediate containers <span class="token parameter variable">--iidfile</span> string Write the image ID to the <span class="token function">file</span> <span class="token parameter variable">--isolation</span> string Container isolation technology <span class="token parameter variable">--label</span> list Set metadata <span class="token keyword">for</span> an image -m, <span class="token parameter variable">--memory</span> bytes Memory limit --memory-swap bytes Swap limit equal to memory plus swap: <span class="token string">'-1'</span> to <span class="token builtin class-name">enable</span> unlimited swap <span class="token parameter variable">--network</span> string Set the networking mode <span class="token keyword">for</span> the RUN instructions during build <span class="token punctuation">(</span>default <span class="token string">"default"</span><span class="token punctuation">)</span> --no-cache Do not use cache when building the image <span class="token parameter variable">--pull</span> Always attempt to pull a newer version of the image -q, <span class="token parameter variable">--quiet</span> Suppress the build output and print image ID on success <span class="token parameter variable">--rm</span> Remove intermediate containers after a successful build <span class="token punctuation">(</span>default <span class="token boolean">true</span><span class="token punctuation">)</span> --security-opt strings Security options --shm-size bytes Size of /dev/shm -t, <span class="token parameter variable">--tag</span> list Name and optionally a tag <span class="token keyword">in</span> the <span class="token string">'name:tag'</span> <span class="token function">format</span> <span class="token parameter variable">--target</span> string Set the target build stage to build. <span class="token parameter variable">--ulimit</span> <span class="token builtin class-name">ulimit</span> Ulimit options <span class="token punctuation">(</span>default <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>root@fengzhao-linux-server:~<span class="token comment">#</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<summary type="html"><h2 id="Docker-概述"><a href="#Docker-概述" class="headerlink" title="Docker 概述"></a>Docker 概述</h2><p>Docker是一个开发,运输和运行应用程序的开放平台。 Docker使您可以将应用程序与基础架构分离,以便快速交付软件。使用Docker,您可以像管理应用程序一样管理基础架构。通过利用Docker的方法快速发送,测试和部署代码,您可以显着减少编写代码和在生产中运行代码之间的延迟。</p>
<p>Docker提供了在称为容器的松散隔离环境中打包和运行应用程序的功能。隔离和安全性允许您在给定主机上同时运行多个容器。容器是轻量级的,因为它们不需要管理程序的额外负载,而是直接在主机内核中运行。这意味着您可以在给定硬件组合上运行比使用虚拟机时更多的容器。您甚至可以在实际虚拟机的主机中运行Docker容器!</p></summary>
<category term="docker" scheme="https://fengzhao.me/tags/docker/"/>
</entry>
<entry>
<title>浅谈MySQL日志文件系统</title>
<link href="https://fengzhao.me/2018/06/04/2018-06-04-%E6%B5%85%E8%B0%88MySQL%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/"/>
<id>https://fengzhao.me/2018/06/04/2018-06-04-%E6%B5%85%E8%B0%88MySQL%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/</id>
<published>2018-06-04T03:47:26.000Z</published>
<updated>2018-10-30T16:43:14.321Z</updated>
<content type="html"><![CDATA[<h1 id="MySQL日志文件系统"><a href="#MySQL日志文件系统" class="headerlink" title="MySQL日志文件系统"></a>MySQL日志文件系统</h1><p>日志是mysql数据库的重要组成部分。日志文件中记录着mysql数据库运行期间发生的变化;也就是说用来记录mysql数据库的客户端连接状况、SQL语句的执行情况和错误信息等。当数据库遭到意外的损坏时,可以通过日志查看文件出错的原因,并且可以通过日志文件进行数据恢复。</p><h1 id="MySQL日志分类"><a href="#MySQL日志分类" class="headerlink" title="MySQL日志分类"></a>MySQL日志分类</h1><p>MySQL日志大致可以分为一下几种类型:</p><span id="more"></span><table><thead><tr><th>日志类型</th><th>日志作用</th></tr></thead><tbody><tr><td>错误日志</td><td>记录启动、运行或停止mysqld时出现的问题。</td></tr><tr><td>通用查询日志</td><td>记录建立的客户端连接和执行的语句。</td></tr><tr><td>二进制日志</td><td>记录所有更改数据的语句。还用于复制。</td></tr><tr><td>中继日志</td><td>只有主从复制的slave服务器才会有,记录的是来自主服务器的数据变化;</td></tr><tr><td>慢查询日志</td><td>记录所有执行时间超过long_query_time秒的所有查询或不使用索引的查询。</td></tr><tr><td>DDL日志</td><td>也叫元数据日志,记录的是通过DDL进行的元数据操作;</td></tr></tbody></table><h1 id="错误日志"><a href="#错误日志" class="headerlink" title="错误日志"></a>错误日志</h1><p>MySQL的错误日志,默认是开启并无法关闭,一般存放在在数据目录中,以主机名.err格式命名,也可以用 可以用–log-error[=file_name]选项来开启mysql错误日志,该选项指定mysqld保存错误日志文件的位置。错误日志一般是文本文件,可以直接tail打开进行分析查看。</p><p>错误日志不仅仅记录着错误信息,它记录的事件有:</p><ul><li>服务器启动和关闭过程中的信息</li><li>服务器运行过程中的错误信息</li><li>事件调度器运行一个事件时产生的信息</li><li>(如果被配置为从服务器)启动从服务器进程时产生的信息</li></ul><p>查看错误日志存放路径:</p><pre>mysql> SHOW VARIABLES LIKE 'log_error%';+---------------+---------------------------------+| Variable_name | Value |+---------------+---------------------------------+| log_error | /var/lib/mysql/iZ2319y43bmZ.err |+---------------+---------------------------------+1 row in set (0.00 sec)mysql> </pre><h1 id="通用查询日志"><a href="#通用查询日志" class="headerlink" title="通用查询日志"></a>通用查询日志</h1><p>通用查询日志记录了数据库执行的命令。而不仅仅只是select语句,当开启查询日志之后,数据库可能需要不停地写入查询日志,会增加很多磁盘I/O,所以除非是出于调试目的,一般不建议开启查询日志。</p><p>如果需要可以手动开启。如果开启了查询日志,可以通过如下3种方式输出在查询日志</p><ul><li>将查询日志存放于指定的日志文件中。</li><li>将查询日志存放于mysql.general_log表中。</li><li>将查询日志同时存放于指定的日志文件与mysql库的general_log表中。</li></ul><p>查看通用查询日志相关信息:</p><pre>mysql> show variables where variable_name like '%general_log%' or variable_name='log_output';+------------------+---------------------------------+| Variable_name | Value |+------------------+---------------------------------+| general_log | OFF || general_log_file | /var/lib/mysql/iZ2319y43bmZ.log || log_output | FILE |+------------------+---------------------------------+3 rows in set (0.00 sec)</pre><p>log_oupt可以log_output:表示当查询日志开启以后,以哪种方式存放,log_output可以设置为4种值,FILE、TABLE 、FILE,TABLE、NONE。</p><p>此值为NONE时表示不记录查询日志,即使general_log设置为ON,如果log_output设置为NONE,也不会记录查询日志,其实,log_output不止控制用户查询日志的输出,慢查询日志的输出也是由此参数进行控制,也就是说,log_output设置为file,就表示查询日志和慢查询日志都存放到对应的文件中,设置为table,查询日志和慢查询日志就都存放在对应的数据库表中。</p><h1 id="慢查询日志"><a href="#慢查询日志" class="headerlink" title="慢查询日志"></a>慢查询日志</h1><p>MySQL慢查询日志是MySQL提供的一种日志记录,用来记录执行时长超过指定时长的查询语句,具体指运行时间超过 long_query_time 值的 SQL 语句,则会被记录到慢查询日志中。</p><p>long_query_time 默认值是 10 ,单位是 s,即默认是 10秒 。默认情况下,MySQL数据库并不会开启慢查询日志,需要手动设置这个参数。</p><p>慢查询日志开销比较小,通过对慢查询的分析可以到系统的性能问题,找到相关的sql语句并进一步优化,因为或多或少都有一些性能开销,所以如果不是为了调优,一般建议不用开启。</p><pre>mysql> SHOW VARIABLES LIKE 'slow_query_log%';+---------------------+--------------------------------------------------+| Variable_name | Value |+---------------------+--------------------------------------------------+| slow_query_log | OFF || slow_query_log_file | /usr/local/var/mysql/upstreamdeMac-mini-slow.log |+---------------------+--------------------------------------------------+</pre><h2 id="慢查询相关配置"><a href="#慢查询相关配置" class="headerlink" title="慢查询相关配置"></a>慢查询相关配置</h2><pre># slow loggingslow_query_log=1 //是否启用慢查询日志,[1 | 0] 或者 [ON | OFF], MySQL 5.6以前此参数应该为log_slow_querieslog-output=FILE // 指定用文件存储或者用数据表存储,与通用查询日志共用这个参数,日志记录到系统的专用日志表中,要比记录到文件耗费更多的系统资源,因此对于需要启用慢查询日志,又需要能够获得更高的系统性能,那么建议优先记录到文件。slow_query_log_file //指定文件存储路径和文件名,一般在数据目录下,默认是/var/lib/mysql/hostname-slow.log。long_query_time=10 //慢查的时长单位为秒,可以精确到小数点后6位(微秒)。log_queries_not_using_indexes //没有使用到索引的语句,是否被记录到慢查询日志中。log_timestamps //主要是控制 error log、genera log,等等记录日志的显示时间参数,默认时间戳是utc时间,与北京时间相差8h,一般我们会设置成SYSTEMd为操作系统时间。</pre><h2 id="慢查询日志分析"><a href="#慢查询日志分析" class="headerlink" title="慢查询日志分析"></a>慢查询日志分析</h2><p>慢查询一般用下面两种工具来进行分析:</p><h3 id="mysqldumpslow"><a href="#mysqldumpslow" class="headerlink" title="mysqldumpslow"></a>mysqldumpslow</h3><p>mysqldumpslow是官方自带的慢查询日志分析工具,用perl编写,可以用mysqldumpslow -h来看看具体用法。</p><pre>[root@iZ2319y43bmZ ~]# mysqldumpslow -hOption h requires an argumentERROR: bad optionUsage: mysqldumpslow [ OPTS... ] [ LOGS... ]Parse and summarize the MySQL slow query log. Options are --verbose verbose --debug debug --help write this text to standard output -v verbose -d debug -s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default al: average lock time ar: average rows sent at: average query time c: count l: lock time r: rows sent t: query time -r reverse the sort order (largest last instead of first) -t NUM just show the top n queries -a don't abstract all numbers to N and strings to 'S' -n NUM abstract numbers with at least n digits within names -g PATTERN grep: only consider stmts that include this string -h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard), default is '*', i.e. match all -i NAME name of server instance (if using mysql.server startup script) -l don't subtract lock time from total time[root@iZ2319y43bmZ ~]# </pre><p>主要参数:</p><ul><li>-v、–verbose : 在详细模式下运行,打印有关该程序的更多信息。</li><li>-d、–debug : 在调试模式下运行。</li><li>-s [sort_type] : sort_type 是信息排序的依据<ul><li>al:average lock time,按平均等待锁的时间排序</li><li>ar:average rows sent,按平均发给客户端的行总数排序</li><li>at:average query time,按平均查询时间排序</li><li>c:count,按出现总次数排序</li><li>l:lock time,按等待锁的时间排序</li><li>r:rows sent,按扫描的行总数排序</li><li>t:query time,按累计总耗费时间排序</li></ul></li><li>-r :倒序信息排序</li><li>-t NUM :只显示 top n 条查询</li><li>-g PATTERN : 根据字符串筛选慢查询日志,可写正则匹配,大小写不敏感。</li><li>-h HOSTNAME : 根据服务器名称选择慢查询日志</li><li>-i NAME : 根据服务器 MySQL 实例名称选择慢查询日志</li><li>-l : 不要将总时间减去锁定时间</li></ul><p> </p><pre>[root@iZ2319y43bmZ mysql]# mysqldumpslow slowquery_2018052511.logReading mysql slow query log from slowquery_2018052511.logCount: 1 Time=1.23s (3s) Lock=0.00s (0s) Rows=143.0 (143), doyd[doyd]@[119.145.8.228] show full tables from `kpzs_v2` where table_type = 'S'[root@iZ2319y43bmZ mysql]# </pre><p>它主要统计慢查询语句的如下信息:</p><ul><li>出现次数(count)</li><li>执行最长时间,耗费总时间</li><li>等待锁的时间</li><li>发送给客户端的行数</li><li>执行该语句的客户端地址</li><li>执行的原语句</li></ul><h3 id="percona-toolkit"><a href="#percona-toolkit" class="headerlink" title="percona-toolkit"></a>percona-toolkit</h3><p>percona-toolkit 是一组高级命令行工具的集合,用prel语言编写,需要自行手工安装,安装可以参考<a href="https://www.percona.com/doc/percona-toolkit/LATEST/index.html">官网文档</a>,用来执行各种通过手工执行非常复杂和麻烦的 mysql 和系统任务。percona-toolkit中关于慢查询,主要是pt-query-digest 这个工具,这个工具可以把分析结果输出到表中,做慢查询监控时,可以考虑写脚本去分析慢查询并将结果输出到监控库,设置脚本定期执行即可。如果想直接定位分析,一般pt-query-digest slowquery_yyyymmddhh.log即可</p><p>用法:</p><pre>pt-query-digest [OPTIONS] [FILES] [DSN]</pre><p>参数说明:</p><ul><li>–create-review-table : 当使用–review参数把分析结果输出到表中时,如果没有表就自动创建。</li><li>–create-history-table : 当使用–history参数把分析结果输出到表中时,如果没有表就自动创建。</li><li>–filter : 对输入的慢查询按指定的字符串进行匹配过滤后再进行分析</li><li>–limit : 限制输出结果百分比或数量,默认值是20,即将最慢的20条语句输出,如果是50%则按总响应时间占比从大到小排序,输出到总和达到50%位置截止。</li><li>–host : mysql服务器地址</li><li>–user : mysql用户名</li><li>-password : mysql用户密码</li><li>–history : 将分析结果保存到表中,分析结果比较详细,下次再使用–history时,如果存在相同的语句,且查询所在的时间区间和历史表中的不同,则会记录到数据表中,可以通过查询同一CHECKSUM来比较某类型查询的历史变化。</li><li>–review : 将分析结果保存到表中,这个分析只是对查询条件进行参数化,一个类型的查询一条记录,比较简单。当下次使用–review时,如果存在相同的语句分析,就不会记录到数据表中。</li><li>–output : 分析结果输出类型,值可以是report(标准分析报告)、slowlog(Mysql slow log)、json、json-anon,一般使用report,以便于阅读。</li><li>–since : 从什么时间开始分析,值为字符串,可以是指定的某个 “yyyy-mm-dd [hh:mm:ss]” 格式的时间点,也可以是简单的一个时间值:s(秒)、h(小时)、m(分钟)、d(天),如12h就表示从12小时前开始统计。<br>–until : 截止时间,配合—since可以分析一段时间内的慢查询。</li></ul><pre>[root@iZ2319y43bmZ mysql]# pt-query-digest slowquery_2018060110.log# 240ms user time, 20ms system time, 24.45M rss, 204.81M vsz# Current date: Mon Jun 4 10:08:33 2018# Hostname: iZ2319y43bmZ# Files: slowquery_2018060110.log# Overall: 1 total, 1 unique, 0 QPS, 0x concurrency ______________________# Time range: all events occurred at 2018-06-01 10:49:59# Attribute total min max avg 95% stddev median# ============ ======= ======= ======= ======= ======= ======= =======# Exec time 1s 1s 1s 1s 1s 0 1s# Lock time 75us 75us 75us 75us 75us 0 75us# Rows sent 143 143 143 143 143 0 143# Rows examine 143 143 143 143 143 0 143# Query size 42 42 42 42 42 0 42# Profile# Rank Query ID Response time Calls R/Call V/M Item# ==== ================== ============= ===== ====== ===== ===========# 1 0xFBE0E38DA0F69470 1.0037 100.0% 1 1.0037 0.00 SHOW TABLES# Query 1: 0 QPS, 0x concurrency, ID 0xFBE0E38DA0F69470 at byte 0 ________# This item is included in the report because it matches --limit.# Scores: V/M = 0.00# Time range: all events occurred at 2018-06-01 10:49:59# Attribute pct total min max avg 95% stddev median# ============ === ======= ======= ======= ======= ======= ======= =======# Count 100 1# Exec time 100 1s 1s 1s 1s 1s 0 1s# Lock time 100 75us 75us 75us 75us 75us 0 75us# Rows sent 100 143 143 143 143 143 0 143# Rows examine 100 143 143 143 143 143 0 143# Query size 100 42 42 42 42 42 0 42# String:# Databases kpzs# Hosts localhost# Users doyd# Query_time distribution# 1us# 10us# 100us# 1ms# 10ms# 100ms# 1s ################################################################# 10s+# Tables# SHOW TABLE STATUS FROM `kpzs` LIKE 'kpzs_v2'\G# SHOW CREATE TABLE `kpzs`.`kpzs_v2`\GSHOW /*!50002 FULL*/ TABLES FROM `kpzs_v2`\G[root@iZ2319y43bmZ mysql]# </pre><h1 id="二进制日志文件"><a href="#二进制日志文件" class="headerlink" title="二进制日志文件"></a>二进制日志文件</h1><p>二进制日志(binlog)记录 MySQL 数据库中所有与更新相关的操作,即二进制日志记录了所有的 DDL(数据定义语言)语句和 DML(数据操纵语言)语句,但是不包括数据查询语句。常用于恢复数据库和主从复制。</p><h2 id="二进制日志作用"><a href="#二进制日志作用" class="headerlink" title="二进制日志作用"></a>二进制日志作用</h2><p>假如备份策略是每天0:00备份一次,那么在本次备份后,下面备份前的这个时间段内某一时刻,数据库崩溃,如果只是依靠备份文件来恢复,中间那段时间的数据就丢失了,所以还需要依靠二进制日志文件来对0:00之后的所有更新相关操作进行replay一次,这样就可以将数据恢复到崩溃时的状态,这与oracle中的归档日志是类似的。</p><h2 id="二进制日志类型"><a href="#二进制日志类型" class="headerlink" title="二进制日志类型"></a>二进制日志类型</h2><ul><li><p>statement模式:记录对数据库实例的更新语句,例如对语句update A set user=’test’,这种模式只记录该语句,这种模式的优点是日志量小,IO压力小,性能较高,缺点是记录的不够精确,例如,某些sql语句带了一些函数如user(),不同的数据库用户执行sql可能会操作不同的数据,这种情况下,只是记录语句是不够的,在恢复的时候,并不知道该语句当初是哪个用户执行的,所以并不能保证恢复与操作完全一致。</p></li><li><p>row模式:记录对语句所修改到的行,以及对行的具体修改,例如,update A set user=’test’,这条语句所影响到的行,以及修改,都会记录到二进制文件中,如果A表中有1000行数据,那么这一千行数据都会</p></li><li><p>mixed模式:在如下情况,系统会自动从statement模式切换刀片</p></li></ul><h2 id="查看二进制相关配置"><a href="#查看二进制相关配置" class="headerlink" title="查看二进制相关配置"></a>查看二进制相关配置</h2><pre>mysql> SHOW VARIABLES LIKE 'log_bin%';+---------------------------------+-------+| Variable_name | Value |+---------------------------------+-------+| log_bin | OFF || log_bin_basename | || log_bin_index | || log_bin_trust_function_creators | OFF || log_bin_use_v1_row_events | OFF |+---------------------------------+-------+5 rows in set (0.04 sec)mysql> </pre><p></p>]]></content>
<summary type="html"><h1 id="MySQL日志文件系统"><a href="#MySQL日志文件系统" class="headerlink" title="MySQL日志文件系统"></a>MySQL日志文件系统</h1><p>日志是mysql数据库的重要组成部分。日志文件中记录着mysql数据库运行期间发生的变化;也就是说用来记录mysql数据库的客户端连接状况、SQL语句的执行情况和错误信息等。当数据库遭到意外的损坏时,可以通过日志查看文件出错的原因,并且可以通过日志文件进行数据恢复。</p>
<h1 id="MySQL日志分类"><a href="#MySQL日志分类" class="headerlink" title="MySQL日志分类"></a>MySQL日志分类</h1><p>MySQL日志大致可以分为一下几种类型:</p></summary>
<category term="MySQL" scheme="https://fengzhao.me/tags/mysql/"/>
</entry>
<entry>
<title>理解数据库事务和锁</title>
<link href="https://fengzhao.me/2018/05/10/2018-05-10-DataBaseTransaction/"/>
<id>https://fengzhao.me/2018/05/10/2018-05-10-DataBaseTransaction/</id>
<published>2018-05-10T03:39:47.000Z</published>
<updated>2019-08-15T15:15:16.286Z</updated>
<content type="html"><![CDATA[<h1 id="数据库事务"><a href="#数据库事务" class="headerlink" title="数据库事务"></a>数据库事务</h1><blockquote><p>数据库事务(Database Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。</p></blockquote><p>一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:</p><blockquote><ul><li>为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。<br>当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。</li></ul></blockquote><blockquote><ul><li>当事务被提交给了DBMS(数据库管理系统),则DBMS(数据库管理系统)需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要被回滚,回到事务执行前的状态;同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。</li></ul></blockquote><span id="more"></span><p>但在现实情况下,失败的风险很高。在一个数据库事务的执行过程中,有可能会遇上事务操作失败、数据库系统/操作系统失败,甚至是存储介质失败等情况。这便需要DBMS对一个执行失败的事务执行恢复操作,将其数据库状态恢复到一致状态(数据的一致性得到保证的状态)。为了实现将数据库状态恢复到一致状态的功能,DBMS通常需要维护事务日志以追踪事务中所有影响数据库数据的操作。</p><h2 id="事务特性"><a href="#事务特性" class="headerlink" title="事务特性"></a>事务特性</h2><p>并非任意的对数据库的操作序列都是数据库事务。数据库事务拥有以下四个特性,习惯上被称之为ACID特性。</p><blockquote><ul><li>原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。</li><li>一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。</li><li>隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。</li><li>持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。</li></ul></blockquote><h2 id="事务举例"><a href="#事务举例" class="headerlink" title="事务举例"></a>事务举例</h2><p>用一个常用的“A账户向B账号汇钱”的例子来说明如何通过数据库事务保证数据的准确性和完整性。熟悉关系型数据库事务的都知道从帐号A到帐号B需要6个操作:</p><p>1、从A账号中把余额读出来(500)。</br><br>2、对A账号做减法操作(500-100)。</br><br>3、把结果写回A账号中(400)。</br><br>4、从B账号中把余额读出来(500)。</br><br>5、对B账号做加法操作(500+100)。</br><br>6、把结果写回B账号中(600)。</br></p><h3 id="原子性:"><a href="#原子性:" class="headerlink" title="原子性:"></a>原子性:</h3><p>保证1-6所有过程要么都执行,要么都不执行。一旦在执行某一步骤的过程中发生问题,就需要执行回滚操作。 假如执行到第五步的时候,B账户突然不可用(比如被注销),那么之前的所有操作都应该回滚到执行事务之前的状态。</p><h3 id="一致性"><a href="#一致性" class="headerlink" title="一致性"></a>一致性</h3><p>在转账之前,A和B的账户中共有500+500=1000元钱。在转账之后,A和B的账户中共有400+600=1000元。也就是说,数据的状态在执行该事务操作之后从一个状态改变到了另外一个状态。同时一致性还能保证账户余额不会变成负数等。</p><h3 id="隔离性"><a href="#隔离性" class="headerlink" title="隔离性"></a>隔离性</h3><p>在A向B转账的整个过程中,只要事务还没有提交(commit),查询A账户和B账户的时候,两个账户里面的钱的数量都不会有变化。<br>如果在A给B转账的同时,有另外一个事务执行了C给B转账的操作,那么当两个事务都结束的时候,B账户里面的钱应该是A转给B的钱加上C转给B的钱再加上自己原有的钱。</p><h3 id="持久性"><a href="#持久性" class="headerlink" title="持久性"></a>持久性</h3><p>一旦转账成功(事务提交),两个账户的里面的钱就会真的发生变化(会把数据写入数据库做持久化保存)。</p><h1 id="数据库读现象"><a href="#数据库读现象" class="headerlink" title="数据库读现象"></a>数据库读现象</h1><p>“读现象”是多个事务并发执行时,在读取数据方面可能碰到的状况。先了解它们有助于理解各隔离级别的含义。其中包括脏读、不可重复读和幻读。</p><h2 id="脏读"><a href="#脏读" class="headerlink" title="脏读"></a>脏读</h2><p>脏读是指在数据库访问中,当一个事务A正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务B也访问这个数据,然后使用了这个数据。然后A又进行回滚操作,则事务B访问的数据是无效的。即上述转账例子中,转账事务进行到一半的时候,此时另外一个事务去读了A账号的余额,然后转账事务回滚,A的余额变回以前的值。此时后面读余额的事务即为脏读。</p><h2 id="不可重复读"><a href="#不可重复读" class="headerlink" title="不可重复读"></a>不可重复读</h2><p>不可重复读,即在一个事务中分多次读同一数据,但是在前后两次读取之间,另外一个事务也在访问该数据,可能进行了修改,这样就造成了第一次事务的前后两次读取的数据不一致的现象。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。</p><h2 id="幻读"><a href="#幻读" class="headerlink" title="幻读"></a>幻读</h2><p>幻读与不可重复读之间很容易让人混淆。举一个简单的例子来理解一下幻读:</br></p><p>users: id 主键</p><p>1、事务A:select * from users where id = 1;</br><br>2、事务B:insert into <code>users</code>(<code>id</code>, <code>name</code>) values (1, ‘big cat’);</br><br>3、事务A:insert into <code>users</code>(<code>id</code>, <code>name</code>) values (1, ‘big cat’);</br></p><blockquote><p>A :主事务,检测表中是否有 id 为 1 的记录,没有则插入,这是我们期望的正常业务逻辑。<br>B :干扰事务,目的在于扰乱 T1 的正常的事务执行。</br></p></blockquote><p>关于这种现象,可以称之为幻读。在mysql中,关于这两个事务,可能会有两个结果,A全部B失败,或者B成功A失败。这取决于数据库的不同隔离级别。</p><p>例如,在 RR 隔离级别下,1、2是正常执行的,3则会报主键冲突,对于A事务的业务是执行失败的,这里事务A就发生了幻读,因为它读取的数据状态不能满足它的下一步业务。</p><h1 id="数据库并发控制"><a href="#数据库并发控制" class="headerlink" title="数据库并发控制"></a>数据库并发控制</h1><h2 id="并发控制"><a href="#并发控制" class="headerlink" title="并发控制"></a>并发控制</h2><blockquote><p> 在计算机科学,特别是程序设计、操作系统、多处理机和数据库等领域,并发控制(Concurrency control)是确保及时纠正由并发操作导致的错误的一种机制。</p></blockquote><p>我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式。同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力。所以对于加锁的处理,可以说就是数据库对于事务处理的精髓所在。</p><p>数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。前面关于事务的概念以及读现象举的例子其实都是并发的场景。再举例讨论一个实际场景中的例子:两个火车票代售点,同时读取12306数据库中的某趟列车的车票数量为X,然后同时卖出一张票,然后同时提交了X-1到数据库中,这样就造成了卖了两张票,而库中记录只减了一张。生这种情况的原因是因为两个事务读入同一数据并同时修改,其中一个事务提交的结果破坏了另一个事务提交的结果,导致其数据的修改被丢失,破坏了事务的隔离性。并发控制要解决的就是这类问题。</p><h2 id="数据库隔离"><a href="#数据库隔离" class="headerlink" title="数据库隔离"></a>数据库隔离</h2><p>在DBMS中,事务保证了一个操作序列可以全部都执行或者全部都不执行(原子性),从一个状态转变到另外一个状态(一致性)。由于事务满足久性。所以一旦事务被提交之后,数据就能够被持久化下来,又因为事务是满足隔离性的,所以,当多个事务同时处理同一个数据的时候,多个事务直接是互不影响的,所以,在多个事务并发操作的过程中,如果控制不好隔离级别,就有可能产生脏读、不可重复读或者幻读等读现象。</p><p>ANSI/ISO SQL定义的标准隔离级别有四种,从高到底依次为:可序列化(Serializable)、可重复读(Repeatable reads)、提交读(Read committed)、未提交读(Read uncommitted)。</br></p><table><thead><tr><th align="left">隔离级别</th><th align="left">脏读(Dirty Read)</th><th align="left">不可重复读(NonRepeatable Read)</th><th align="left">幻读(Phantom Read)</th></tr></thead><tbody><tr><td align="left">未提交读(Read uncommitted)</td><td align="left">可能</td><td align="left">可能</td><td align="left">可能</td></tr><tr><td align="left">已提交读(Read committed)</td><td align="left">不可能</td><td align="left">可能</td><td align="left">可能</td></tr><tr><td align="left">可重复读(Repeatable read)</td><td align="left">不可能</td><td align="left">不可能</td><td align="left">可能</td></tr><tr><td align="left">可串行化(Serializable )</td><td align="left">不可能</td><td align="left">不可能</td><td align="left">不可能</td></tr></tbody></table><blockquote><ul><li>未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据</li><li>提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)</li><li>可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。 在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读</li><li>串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞</li></ul></blockquote><h2 id="锁"><a href="#锁" class="headerlink" title="锁"></a>锁</h2><p>当并发事务同时访问一个资源时,有可能导致数据不一致,因此需要一种机制来将数据访问顺序化,以保证数据库数据的一致性。锁就是其中的一种机制。可以简单理解为当某个事务在操作开始时,锁定某一个对象,在这个事务操作结束之前,不允许其他事务操作这个对象。下面是常见的锁分类:</p><h3 id="锁的分类"><a href="#锁的分类" class="headerlink" title="锁的分类"></a>锁的分类</h3><p>1、按照锁定的对象的粒度划分:表级锁、行级锁、页级锁(mysql)</br><br>2、按锁级别划分:共享锁(shared lock,读锁,shared lock)、排他锁(exclusive lock,写锁,write lock)</br><br>3、按使用方式划分,可分为乐观锁、悲观锁</br></p><p>各种锁的比较:</p><blockquote><ul><li>表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。</li><li>行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。</li><li>页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。</li></ul></blockquote><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><blockquote><p>淘宝MySQL博客 <a href="http://mysql.taobao.org/monthly/2016/01/01/">http://mysql.taobao.org/monthly/2016/01/01/</a> </br><br>美团点评技术团队 <a href="https://tech.meituan.com/innodb-lock.html">https://tech.meituan.com/innodb-lock.html</a></p></blockquote>]]></content>
<summary type="html"><h1 id="数据库事务"><a href="#数据库事务" class="headerlink" title="数据库事务"></a>数据库事务</h1><blockquote>
<p>数据库事务(Database Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。</p>
</blockquote>
<p>一个数据库事务通常包含了一个序列的对数据库的读&#x2F;写操作。它的存在包含有以下两个目的:</p>
<blockquote>
<ul>
<li>为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。<br>当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>当事务被提交给了DBMS(数据库管理系统),则DBMS(数据库管理系统)需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要被回滚,回到事务执行前的状态;同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。</li>
</ul>
</blockquote></summary>
<category term="数据库" scheme="https://fengzhao.me/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
</entry>
</feed>