<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; 程序相关</title>
	<atom:link href="https://www.phppan.com/category/%e7%a8%8b%e5%ba%8f%e7%9b%b8%e5%85%b3/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sun, 10 May 2026 02:26:45 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>在 AWS 的 EC2 上搭建换装模型 ComfyUI OOTDiffusion</title>
		<link>https://www.phppan.com/2024/06/aws-ec2-comfyui-ootdiffusion/</link>
		<comments>https://www.phppan.com/2024/06/aws-ec2-comfyui-ootdiffusion/#comments</comments>
		<pubDate>Wed, 26 Jun 2024 09:03:43 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[comfyUI]]></category>
		<category><![CDATA[OOTDiffusion]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=2246</guid>
		<description><![CDATA[项目地址：https://github.com/AuroBit/ComfyUI-OOTDiffusion 在  [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>项目地址：https://github.com/AuroBit/ComfyUI-OOTDiffusion</p>
<p>在 AWS 上购买 <span style="color: #16191f;">g4dn.xlarge ，带有一个 gpu  nvidia 显卡</span></p>
<p>初始化机器，如果没有选带 nvidia 驱动的操作系统，需要自己安装 nvidia 的显卡</p>
<p><!--?xml version="1.0" encoding="UTF-8"?--></p>
<div></div>
<div></div>
<div style="padding-left: 30px;">sudo apt-get update #更新软件列表</div>
<div style="padding-left: 30px;">sudo apt-get install g++</div>
<div style="padding-left: 30px;">sudo apt-get install gcc</div>
<div style="padding-left: 30px;">sudo apt-get install make ubuntu-drivers</div>
<div style="padding-left: 30px;">sudo vim /etc/modprobe.d/blacklist-nouveau.conf</div>
<div style="padding-left: 30px;">blacklist nouveau</div>
<div style="padding-left: 30px;">options nouveau modeset=0</div>
<div style="padding-left: 30px;">sudo update-initramfs -u #更新系统</div>
<div style="padding-left: 30px;">sudo reboot # 重启</div>
<div style="padding-left: 30px;"></div>
<div style="padding-left: 30px;"></div>
<div style="padding-left: 30px;">lsmod | grep nouveau</div>
<div style="padding-left: 30px;">sudo apt-get remove &#8211;purge nvidia*</div>
<div style="padding-left: 30px;">ubuntu-drivers devices</div>
<div style="padding-left: 30px;">sudo add-apt-repository ppa:graphics-drivers/ppa</div>
<div style="padding-left: 30px;">sudo apt-get update</div>
<div style="padding-left: 30px;">sudo apt-get install nvidia-driver-535 #此处数字要对应上面查询到的版本号</div>
<div style="padding-left: 30px;">sudo apt-get install mesa-common-dev</div>
<div style="padding-left: 30px;">sudo reboot # 重启</div>
<div style="padding-left: 30px;"></div>
<div style="padding-left: 30px;"></div>
<div style="padding-left: 30px;">nvidia-smi</div>
<div></div>
<div></div>
<p>Clone 项目并安装相关依赖：</p>
<pre style="color: #1f2328;">conda create -n ootd python=3.10
conda activate ootd

conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia

<span class="pl-c" style="color: var(--color-prettylights-syntax-comment);"># Install nvcc compiler for torch cpp extensions</span>
conda install cuda-nvcc -c nvidia

#  Clone ComfyUI
git clone https://github.com/comfyanonymous/ComfyUI.git
<span class="pl-c" style="color: var(--color-prettylights-syntax-comment);"># Clone to custom_nodes</span>
git clone https://github.com/AuroBit/ComfyUI-OOTDiffusion.git custom_nodes/ComfyUI-OOTDiffusion

<span class="pl-c" style="color: var(--color-prettylights-syntax-comment);"># Install dependencies</span>
pip install -r custom_nodes/ComfyUI-OOTDiffusion/requirements.txt</pre>
<p>如果想外网访问，在启动的时候带上 &#8211;listen，如： python main.py &#8211;listen 0.0.0.0</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2024/06/aws-ec2-comfyui-ootdiffusion/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>数据集合类系统如何架构</title>
		<link>https://www.phppan.com/2015/05/data-collections-systems-architecture/</link>
		<comments>https://www.phppan.com/2015/05/data-collections-systems-architecture/#comments</comments>
		<pubDate>Sat, 09 May 2015 17:00:11 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[数据集合]]></category>
		<category><![CDATA[架构]]></category>
		<category><![CDATA[系统架构]]></category>
		<category><![CDATA[高可用]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1926</guid>
		<description><![CDATA[数据集合类系统如何架构 以下内容来源于QCon某高可用架构群聊天记录整理 如果携程网想把旅游信息展示到另一平台 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>数据集合类系统如何架构</p>
<p>以下内容来源于QCon某高可用架构群聊天记录整理</p>
<p>如果携程网想把旅游信息展示到另一平台上 平台和他们的系统数据对接，pull好一些还是post好一些？或者说一个系统只是把好多其他商家的数据集合展示到统一的系统上，这样的系统一般如何架构？</p>
<p>先回答第一个问题：数据对接是pull好一些还是post好一些，这里需要根据实际业务做权衡，如果平台系统很大部分是通过聚合第三方数据再展示，那么比较推荐让第三方post数据，自己设计统一数据规则接口。这种情况，需要考虑自身服务的稳定性了，预防第三方误调用，击垮自身系统。如果只有小部分内容聚合第三方，那就pull，比较好保证自己系统稳定性，不过最终还得把所有的数据转换成自己格式，需要自己开发团队做这块工作。</p>
<p>相比较而言，post时效性高，数据交互少，如果系统会需要各个源的信息，最终也不会只是展示那么简单。如果使用post方案，则需要在接入，数据，读取等方面做隔离，在接入使用mq可以提高吞吐，在读的时候用Cache抗。并且在前期需要考虑好数据存储和数据的量级，因为是第三方的数据，在存储的扩展性方面要有比较好的方案。</p>
<p>最终落地的方案可能是：</p>
<p>按业务隔离，不同的业务相互不影响，拆分子系统；<br />
使用redis和kafka保障高性能，kafka主要一方面用到日志上 一方面用到缓解数据库并发上；<br />
使用nginx和lvs保障高可用；<br />
在后期所有数据进hbase然后用storm做数据流处理和分析</p>
<p>以上这些并不需要一次性做到位，不要过早优化，只需要有一些大的原则，比如隔离，扩展等。小系统会随着业务慢慢演变，最终会变成大系统。在演变的过程中，可能会需要读写分离、业务分布式，分服务，架构就是在这样演变的路上成长起来的。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2015/05/data-collections-systems-architecture/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>微信红包实现原理</title>
		<link>https://www.phppan.com/2015/04/weixin-hongbao/</link>
		<comments>https://www.phppan.com/2015/04/weixin-hongbao/#comments</comments>
		<pubDate>Wed, 29 Apr 2015 23:25:30 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[原子操作]]></category>
		<category><![CDATA[微信红包]]></category>
		<category><![CDATA[红包]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1922</guid>
		<description><![CDATA[微信红包实现原理 以下内容来源于QCon某高可用架构群聊天记录整理 背景：有某个朋友咨询微信红包的架构，在官方 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p class="p1"><span class="s1"><b>微信红包实现原理</b></span></p>
<p class="p2"><span class="s1">以下内容来源于QCon某高可用架构群聊天记录整理 背景：有某个朋友咨询微信红包的架构，在官方或非官方同学的解释和讨论中得出以下讨论内容，在此期间有多个同学发红包做现网算法测试。</span></p>
<p class="p3"><span class="s1"><b>抢红包过程</b></span></p>
<p class="p2"><span class="s1">当有人在群里发了一个N人的红包，总金额M元，后台大概发生的事情如下：</span></p>
<p class="p4"><span class="s1"><b>一、发红包后台操作：</b></span></p>
<ol class="ol1">
<li class="li2"><span class="s1">在数据库中增加一条红包记录，存储到CKV，设置过期时间；</span></li>
<li class="li2"><span class="s1">在Cache（可能是腾讯内部kv数据库，基于内存，有落地，有内核态网络处理模块，以内核模块形式提供服务））中增加一条记录，存储抢红包的人数N</span></li>
</ol>
<p class="p4"><span class="s1"><b>二、抢红包后台操作：</b></span></p>
<ol class="ol1">
<li class="li2"><span class="s1">抢红包分为抢和拆，抢操作在Cache层完成，通过原子减操作进行红包数递减，到0就说明抢光了，最终实际进入后台拆操作的量不大，通过操作的分离将无效请求直接挡在Cache层外面。这里的原子减操作并不是真正意义上的原子减操作，是其Cache层提供的CAS，通过比较版本号不断尝试，存在一定程度上的冲突，冲突的用户会放行，让其进入下一步拆的操作，这也解释了为啥有用户抢到了拆开发现领完了的情况。</span></li>
<li class="li2"><span class="s1">拆红包在数据库完成，通过数据库的事务操作累加已经领取的个数和金额，插入一条领取流水，入账为异步操作，这也解释了为啥在春节期间红包领取后在余额中看不到。拆的时候会实时计算金额，其金额为1分到剩余平均值2倍之间随机数，一个总金额为M元的红包，最大的红包为 M * 2 /N（且不会超过M），当拆了红包后会更新剩余金额和个数。财付通按20万笔每秒入账准备，实际只到8万每秒。</span></li>
</ol>
<p class="p3"><span class="s1"><b>FAQ</b></span></p>
<ol class="ol1">
<li class="li2"><span class="s1">既然在抢的时候有原子减了就不应该出现抢到了拆开没有的情况？<br />
这里的原子减并不是真正意义上的原子操作，是Cache层提供的CAS，通过比较版本号不断尝试。</span></li>
<li class="li2"><span class="s1">cache和db挂了怎么办？<br />
主备 +对账</span></li>
<li class="li2"><span class="s1">有没有红包个数没了，但余额还有情况？<br />
没有，程序最后会有一个take all操作以及一个异步对账保障。</span></li>
<li class="li2"><span class="s1">为什么要分离抢和拆？<br />
总思路是设置多层过滤网，层层筛选，层层减少流量和压力。这个设计最初是因为抢操作是业务层，拆是入账操作，一个操作太重了，而且中断率高。 从接口层面看，第一个接口纯缓存操作，搞压能力强，一个简单查询Cache挡住了绝大部分用户，做了第一道筛选，所以大部分人会看到已经抢完了的提示。</span></li>
<li class="li2"><span class="s1">抢到红包后再发红包或者提现，这里有什么策略吗？<br />
大额优先入账策略</span></li>
<li class="li2"><span class="s1">有没有从数据上证明每个红包的概率是不是均等？<br />
不是绝对均等，就是一个简单的拍脑袋算法。</span></li>
<li class="li2"><span class="s1">拍脑袋算法，会不会出现两个最佳？<br />
会出现金额一样的，但是手气最佳只有一个，先抢到的那个最佳。</span></li>
<li class="li2"><span class="s1">发红包人的钱会不会冻结？<br />
是直接实时扣掉，不是冻结。</span></li>
<li class="li2"><span class="s1">采用实时算出金额是出于什么考虑？<br />
实时效率更高，预算才效率低下。预算还要占额外存储。因为红包只占一条记录而且有效期就几天，所以不需要多大空间。就算压力大时，水平扩展机器是。</span></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2015/04/weixin-hongbao/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>用户参与记录存储的演变</title>
		<link>https://www.phppan.com/2013/07/user-operation-record/</link>
		<comments>https://www.phppan.com/2013/07/user-operation-record/#comments</comments>
		<pubDate>Sun, 14 Jul 2013 08:35:39 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[位存储]]></category>
		<category><![CDATA[分库]]></category>
		<category><![CDATA[分表]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1847</guid>
		<description><![CDATA[有这样一个应用场景：用户有两个连续的操作A和操作B，必须是操作A完成后才能执行操作B，如果操作A没有完成就触发 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>有这样一个应用场景：用户有两个连续的操作A和操作B，必须是操作A完成后才能执行操作B，如果操作A没有完成就触发了操作B，则显示用户需要先执行操作A，即在操作B执行需要查询操作A是否执行过。这里引申出来的问题是，记录用户参与记录，提供针对用户和操作的查询方法。当不同的数据量时，我们的存储方案会大不相同，随着数据的增长，方案不断演变。</p>
<p><strong>1、数据量较小，用户操作行为固定：</strong><br />
     存储：MySQL<br />
     方案：我们以UID为key，一行一个用户，每个用户包括的用户作为列存储，比如UID=100，固定存储为操作A和操作B，则表结构大致如下：<br />
     table_operation<br />
     uid   operation_a  operation_b<br />
     100   1             1      </p>
<p>如果我们要查询用户是否参与A或B时，直接使用SQL: SELECT * FROM table_operation WHERE uid=100  AND  action_a=1就可以达成目标。</p>
<p>    问题：用户操作固定，扩展较难，如果需要增加用户操作行为，则需要增加字段或增加表存储，增加字段的方法在一定的数据量级以下（比如100万）是可行的，如果行为间无关，则增加表存储方案的表现会很不错。</p>
<p><strong>2、数据量较小、用户操作行为不固定：</strong><br />
     与场景1相比，当前场景除了uid这个变量，增加了用户操作变量，即我们需要关注用户和用户操作两个变量。<br />
     存储：MySQL<br />
     方案1：增加操作表，生成操作id，用户操作行为表存储uid和oid。当用户执行一个新的操作时就在操作行为表插入一条记录。其表结构大致如下：</p>
<p>      table_operation_info<br />
      oid    name<br />
      1       operation_a<br />
      2       operation_b</p>
<p>      table_operation<br />
      uid   oid<br />
      1      1<br />
      1      2</p>
<p>     当需要查询用户1是否执行过操作A时，使用SQL：SELECT * FROM table_operation WHERE oid=1 AND  oid=1。<br />
      问题：当用户的操作行为较多时，用户操作行为增长速度很快，数据量也为逐渐增大，可能MySQL单表无法负载。解决方案在后续场景中说明。</p>
<p><strong>3、数据量较大，用户行为固定</strong><br />
       存储：MySQL<br />
       方案：与场景1相比，当前场景不同在于数据量比场景1大，数据量大到MySQL单表负载不过来。此方案解决的就是这个问题，当单表太大时，性价比较高的方法一般是采用分表。我们当前场景的变量是uid，只要依据uid按水平分表即可。</p>
<p><strong>4、数据量较大，用户行为不固定</strong><br />
      存储： MySQL<br />
      方案1：此方案应用于用户的操作行为可以分类的情况，即在场景1的基础上增加两次分表操作，按操作行为类分表和按用户分表。当前方案中我们需要应对两个变量：操作行为和用户。两次分表分别对应这两个变量，按业务规则做操作行为的分表操作，按用户id水平切分减少数据量。</p>
<p>       方案2：此方案是完全的水平分表操作，在场景2的方案基础上，按用户水平切分。</p>
<p><strong>5、数据量超大</strong><br />
     存储： MySQL<br />
     方案1：分库分表，此时一个库已经无法满足需求，规则依据前面的场景实现，根据实际的需求可以考虑把不同的库放不同的机器上。<br />
     方案2：在分库分表的基础上，按位存储，因为一个操作行为有没有执行过是一个是否的状态，即0，1状态，因此我们可以用一个位来存储，64位可以存储64个操作行为的标记。</p>
<p><strong>其它存储</strong><br />
     key-value数据库<br />
     我们的需求实际上并不需要太多的关系型数据库的功能，简单的 k-v数据库就可以实现我们的功能，并且在性能上也会有所提升，毕竟做得少，会快。<br />
     先不管是选择基于内存的，还是非内存的（可以根据实际需求来选择，也可以是热点数据在内存，沉默数据在非内存中），假设我们有足够的空间存储。<br />
     方案1：<br />
        以uid+oid为key，值可以存储状态，也可以只存储是否参与（0和1），但是会存在key太多的情况，特别是当数据量超大时，uid的个数*oid的个数，可能是你无法相像的量级。<br />
     方案2：<br />
        一般来说，用户操作行为的数据量完全小于用户的量级，并且用户操作行为的数据可控。如果要减少key的个数，我们可以使用oid+用户分区索引id作为key，这里所谓的用户分区索引是指将用户以某个数量分成一个区，所有的用户都记录在这个这个区间内，比如以10000为一个区间，则uid为1到9999的用户分到区间0，这里可以以1和0存储用户是否执行了此操作，一个key对应的value初始化存储10000个0。当uid=100的用户执行了某操作，则将第100个0置为1。<br />
      方案3：<br />
           在方案2的基础上，将10000个0转换为10000个01位，假设一个位存储50位，则总共只需要200个。<br />
      方案4：<br />
         当用户量超大时，大多数的用户对于某个操作可能都是没有参与的，则在方案3的基础上我们增加简单的稀疏矩阵压缩，给每个存储位添加索引，当存储值不为0时才会存储。<br />
     方案5：<br />
        我还没想到，期待你的分享</p>
<p><strong>小结</strong></p>
<ul>
<li>随着数据量的增大，总的思路是分冶，当一个表搞不定时分表，当一个库搞不定时分库，当一台机器搞不定时加机器。</li>
<li>对于不同的存储介质选择需要考虑成本和需求，所有的选择都是平衡后的结果。</li>
<li>节省空间，按位存储。</li>
<li>不要过早优化。</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2013/07/user-operation-record/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于Cookie</title>
		<link>https://www.phppan.com/2013/03/about-cookie/</link>
		<comments>https://www.phppan.com/2013/03/about-cookie/#comments</comments>
		<pubDate>Sat, 30 Mar 2013 00:17:15 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[Cookie]]></category>
		<category><![CDATA[Cookies]]></category>
		<category><![CDATA[RFC]]></category>
		<category><![CDATA[RFC2965]]></category>
		<category><![CDATA[session]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1808</guid>
		<description><![CDATA[Cookie是什么 在wiki中Cookie的定义为: Cookie（复数形态Cookies），中文名称为小型 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2>Cookie是什么</h2>
<p>在wiki中Cookie的定义为:  Cookie（复数形态Cookies），中文名称为小型文本文件或小甜饼（貌似这只是一个中文翻译，平时还是直接读的英文），指某些网站为了辨别用户身份而储存在用户本地终端上的数据。</p>
<p>Cookie是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器，是客户端与服务器保持会话的主要手段，其内容总是保存在客户端中，按在客户端中的存储位置，可分为内存Cookie和硬盘Cookie。内存Cookie由浏览器维护，保存在内存中，浏览器关闭后就消失了，其存在时间是短暂的。硬盘Cookie保存在硬盘里，有一个过期时间，除非用户手工清理或到了过期时间，硬盘Cookie不会被删除，其存在时间是长期的。所以，按存在时间，可分为非持久Cookie和持久Cookie。</p>
<p>Cookie被浏览器默认发送到服务器，通过HTTP协议，请求头中以Cookie字段存储客户端的Cookie值，应答头中以Set-Cookie字段应答，<strong>当服务器需要有多个cookie字段写到客户端，则在应答头中将包含多个Set-Cookie字段</strong>。  Cookie的使用非常简单，以PHP为例，在脚本中使用setcookie函数设置对应的key,value值，通过全局变量$_COOKIE直接读取客户端发送过来的Cookie值。</p>
<p>Cookie简单，但是存在一些问题:</p>
<ol>
<li>安全，明文传输内容，容易被篡改。和HTTP一样，只能说看如何使用了，看你存储的是什么了</li>
<li>增加网络流量，加重整个网络的负载。默认浏览器在发送请求时会将本地Cookie的内容通过Cookie字段传输到服务器。所以经常我们会独立静态图片或资源的域名，使其Cookie为空。</li>
<li>大小限制。各浏览器对于单个cookie的大小限制为4096个字节左右，超过大小的内容将被忽略。每个域名下可以存储有30~50个cookie，不同的浏览器，不同的版本这些值不同。为什么会有大小限制，因为cookie会默认发送，当cookie太大时，可能会导致服务器响应出错等。</li>
</ol>
<h2>Cookie的历史</h2>
<p>1993年3月，这样一个春光明媚，面朝大海，春暖花开的时节，现在的网景公司前雇员，当时的NB的网景公司员工Lou  Montulli灵光一闪，Cookie华丽丽的出生了。 Cookie第一次被正式定义是在RFC2109，嗯，这是1997年2月的一天，也许那时还有些冷。在RFC中，Cookie被称为HTTP State Management Mechanism（HTTP 状态管理机制）。  RFC2109在2000年10月被RFC2965过时，而在2011年4月，最新的刚刚火热出炉的RFC6265将RFC2965过时，可谓是长江后浪推前浪，前浪死在沙滩上。另外，RFC2964记录了使用Cookie的最佳实践。</p>
<p>换句话说:Cookie经过了Netscape标准、RFC2109、RFC2965和RFC26265四个标准：</p>
<ul>
<li>Netscape标准：Netscape是最原始的Cookies规范，同时也是RFC2109的基础。尽管如此，还是在很多重要的方面与RFC2109不同，可能需要特定服务器才可以兼容。</li>
<li>RFC2109：  RFC2109是W3C组织第一次推出的官方Cookies标准。理论上，所有使用版本Cookies的服务端都应该使用此标准。HttpClient已经将此标准设定为默认。遗憾的是，许多服务端不正确的实现了标准或者仍然使用Netscape标准。所有有时感到此标准太多于严格。</li>
<li>RFC2965：RFC2965定义了版本2并且尝试去弥补在版本1中Cookie的RFC2109标准的缺点。RFC2965是，并规定RFC2965最终取代RFC2109.  发送RFC2965标准Cookies的服务端，将会使用Set-Cookie2 header添加到Set-Cookie Header信心中，RFC2965  Cookies是区分端口的。</li>
<li>RFC6265：RFC6265主要是干掉了RFC2965，在9.3和9.4小节。另外，增加了HttpOnly字段，指定HttpOnly的Cookie不能被客户端读写，仅供HTTP传输使用，或者就服务器可以读写，浏览器作为客户端需要确保其不能读写。</li>
</ul>
<h2>Cookie和Seesion</h2>
<p>Cookie和Session都用来保存状态信息，做会话处理，都是保存客户端状态的机制，它们都是为了解决HTTP无状态的问题而所做的努力。  Session存储在服务器，一般通过Cookie来存储其生成的唯一ID（seesionID），当Cookie被禁用时，通常用URL回写的机制来替换Cookie。</p>
<p><strong>Cookie和Session有一些不同：</strong></p>
<ol>
<li>存储位置的不同：Cookie将状态保存在客户端，Session将状态保存在服务器端；</li>
<li>与HTTP协议的关系不同：Cookie需要通过网络传输，依赖于HTTP协议，Session并没有在HTTP的协议中定 义；</li>
<li>可用性不同：相对于Cookie，Session在客户端禁用Cookie后还可以通过URL回写机制实现Session会话机制。</li>
<li>安全性不同：因为存储的位置不同，Cookie更容易被篡改，存储在服务器的Session相对来说则安全一些，客户不能随意读取这些内容，除非获到其它用户的了sessionID，这也是XSS攻击会关注的地方。</li>
</ol>
<h2>同源策略</h2>
<p>说到WEB的安全问题就不得不提同源策略。浏览器的同源策略是 Web  安全的基础，所有的主流浏览器都会有相应的实现。同源策略中“源”是一个包含主机名、协议和端口号的三元组，则同源表示：同协议，同域名和同端口，三者都相同。同源策略的出发点是它认为自任何站点装载的信赖内容是不安全的。在同源策略的限制下，浏览器只允许网页中的脚本（如  JavaScript 或 VBScript）访问与之同源的 HTTP 请求和  Cookie。对于Cookie来说，同源策略就限制了网站间的Cookie读写操作。即使在服务器使用setcookie（PHP）函数对其它域名执行Cookie写操作也是无效的。  setcookie的域名是用来指向当前域名或根域名之类的用的,设置Cookie时，如果不指定domain的值，默认就是本域。</p>
<h2>参考资料：</h2>
<ol>
<li>http://wiki.apache.org/HttpComponents/ReferenceMaterials</li>
<li>http://www.cnblogs.com/shepherd2012/archive/2012/08/03/2621797.html</li>
<li>http://zh.wikipedia.org/wiki/Cookie</li>
<li>http://curl.haxx.se/rfc/cookie_spec.html</li>
<li>http://tools.ietf.org/html/rfc6265</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2013/03/about-cookie/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTTP缓存算法</title>
		<link>https://www.phppan.com/2012/12/http-cache-algorithm/</link>
		<comments>https://www.phppan.com/2012/12/http-cache-algorithm/#comments</comments>
		<pubDate>Mon, 10 Dec 2012 00:26:46 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[HTTP协议]]></category>
		<category><![CDATA[HTTP权威指南]]></category>
		<category><![CDATA[HTTP缓存]]></category>
		<category><![CDATA[缓存]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1768</guid>
		<description><![CDATA[HTTP协议缓存的目标是去除许多情况下对于发送请求的需求和去除许多情况下发送完整请求的需求。以不发送请求或减少 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>HTTP协议缓存的目标是去除许多情况下对于发送请求的需求和去除许多情况下发送完整请求的需求。以不发送请求或减少请求传输的数据量来优化整个HTTP架构，此目标的实现可以产生如下好处：</p>
<ul>
<li>减少网络传输的冗余信息量</li>
<li>缓解网络瓶颈的问题</li>
<li>降低对原始服务器的请求量</li>
<li>减少了传送距离，降低了因为距离而产生的时延</li>
</ul>
<p>缓存基本处理过程包括七个步骤。</p>
<ol>
<li>接收 &#8211; 缓存从网络中读取抵达的请求报文</li>
<li>解析 &#8211; 缓存对报文进行解析，提取出URL和各种首部</li>
<li>查询 &#8211; 缓存查看是否有本地副本可用，如果没有，就获取一份副本，并保存在本地</li>
<li>新鲜度检测 &#8211; 缓存查看已缓存副本是否足够新鲜，如果不是，就询问服务器是否有任何更新</li>
<li>创建响应 &#8211; 缓存会用新的首部和已缓存主体来构建一条响应报文</li>
<li>发送 &#8211; 缓存通过网络将响应发回给客户端</li>
<li>日志 &#8211; 缓存可选地创建一个日志文件条目来描述这个事务</li>
</ol>
<p>这里的缓存可以是本地客户端缓存，也可以是代理缓存之类的公共缓存。</p>
<h2>HTTP缓存模型</h2>
<p>HTTP缓存可以在不依赖服务器记住有哪些缓存拥有文档副本，而实现文档的一致。这些机制称为文档过期（document  expiration）和服务器再验证（server revalidation），也可以称它们为截止模型和证实模型。</p>
<p>截止模型是HTTP请求中带上标记文档的过期时间，HTTP协议中使用如下两个字段标记过期时间：</p>
<ul>
<li>Expires字段 &#8211; 指定一个绝对的过期日期。</li>
<li>Cache-control:max-age &#8211; 定义文档的最大使用期，从第一次生成文档到文档不再新鲜，无法使用为止，最大的合法生存时间（单位为s）</li>
</ul>
<p>仅仅使用截止模型还不够，即使文档过期了，也并不意味着当前文档和原始服务器的文档不一致了。此时就到证实模型大显身手的时候了。证实模型需要询问原始服务器文档是否发生了变化。其依赖于HTTP协议的如下字段：</p>
<ul>
<li>If-Modified-Since字段 &#8211;  如果从指定日期之后文档被修改了，就执行请求的方法。可以与Last-modified服务器响应首部配合使用。它告诉服务器只有在客户端缓存了对象的副本后，又服务器对其进行了修改的情况下，才在回复中发送此对象。如果服务器对象没有修改，返回304  Not Modified。如果服务器修改了此对象，发送此对象，返回200 OK。如果服务器删除了些对象，返回404 Not Found。</li>
<li>If-None-Match字段 &#8211; 服务器可以为文档提供特殊的标签（ETag），如果此标签与服务器的标签不一样，就会执行请求的方法。</li>
</ul>
<p>如果服务器应答中包括一个ETag，又包括一个Last-Mofidied值，则客户端在发送请求时使用两种证实机制，并且只有当两种证实机制都满足时才会返回304  Not Modified。</p>
<p>缓存在新鲜度检测时，只需要计算两个值：已缓存副本的使用期和已缓存副本的新鲜生存期。</p>
<h2>HTTP缓存使用期算法</h2>
<p>响应的使用期是服务器发布响应（或通过证实模型再验证）之后经过的总时间。使用期包括了因特网中传输的时间，在中间节点缓存的时间，以及在本地缓存中的停留时间。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">       <span style="color: #666666; font-style: italic;">/*
       * age_value 当代理服务器用自己的头部去响应请求时，Age标明实体产生到现在多长时间了。
       * date_value HTTP 服务器应答中的Date字段 原始服务器
       * request_time 缓存的请求时间
       * response_time 缓存获取应答的时间
       * now 当前时间
       */</span>
&nbsp;
      apparent_age <span style="color: #339933;">=</span> <span style="color: #990000;">max</span>（<span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> response_time <span style="color: #339933;">-</span> date_value）<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//缓存收到响应时响应的年龄 处理时钟偏差存在时，可能为负的情况</span>
&nbsp;
      corrected_received_age <span style="color: #339933;">=</span> <span style="color: #990000;">max</span>（apparent_age<span style="color: #339933;">,</span> age_value）<span style="color: #339933;">;</span>  <span style="color: #666666; font-style: italic;">//  容忍Age首部的错误</span>
&nbsp;
      response_delay <span style="color: #339933;">=</span> response_time <span style="color: #339933;">-</span> request_time<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// 处理网络时延，导致结果保守</span>
&nbsp;
      corrected_initial_age <span style="color: #339933;">=</span> corrected_received_age <span style="color: #339933;">+</span> response_delay<span style="color: #339933;">;</span>
&nbsp;
      resident_time <span style="color: #339933;">=</span> now <span style="color: #339933;">-</span> response_time<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// 本地的停留时间，即收到响应到现在的时间间隔</span>
&nbsp;
      current_age   <span style="color: #339933;">=</span> corrected_initial_age <span style="color: #339933;">+</span> resident_time<span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>因此，完整的使用期计算算法是通过查看Date首部和Age首部来判断响应已使用的时间，再记录其在本地缓存中的停留时间就是总的使用期。除此之外，HTTP协议对时钟偏差和网络时延进行了一补偿，特别是其对网络时延的补偿，可能会重复计算已使用的时间，从而使整个算法产生保守的结果。这种保守的效果时，如果出错了，算法只会使文档看起来比实际使用期要老，并引发再验证。</p>
<h2>HTTP缓存新鲜度算法</h2>
<p>通过已缓存文档的使用期，根据服务器和客户端限制来计算新鲜生存期，就可以确定已缓存的文档是否新鲜。已缓存文档的使用期在前面已经介绍过了，这小节我们来看看新鲜生存期的计算。</p>
<p>为了确定一条响应是保鲜的（fresh）还是陈旧的（stale），我们需要将其保鲜寿命（freshness  lifetime)和年龄(age)进行比较。年龄的计算见13.2.3节，本节讲解怎样计算保鲜寿命，以及判定一个响应是否已经过期。在下面的讨论中，数值可以用任何适于算术操作的形式表示。</p>
<p>与此相关的首部字段包括（按优先级从高到低）：  Cache-Control字段中“max-age”控制指令的值、Expires、Last-Modified、默认最小的生存期。用PHP代码体现如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">    <span style="color: #009933; font-style: italic;">/**
     * $heuristic 启发式过期值应不大于从那个时间开始到现在这段时间间隔的某个分数
     * $Max_Age_value_set  是否存在Max_Age值  Cache-Control字段中“max-age”控制指令的值
     * $Max_Age_value  Max_Age值
     * $Expires_value_set 是否存在Expires值
     * $Expires_value Expires值
     * $Date_value Date头部
     * $default_cache_min_lifetime 
     * $default_cache_max_lifetime
     */</span>
    <span style="color: #000000; font-weight: bold;">function</span> server_freshness_limit<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$Max_Age_value_set</span><span style="color: #339933;">,</span> <span style="color: #000088;">$Max_Age_value</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$Expires_value_set</span><span style="color: #339933;">,</span> <span style="color: #000088;">$Expires_value</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$Date_value</span><span style="color: #339933;">,</span> <span style="color: #000088;">$default_cache_min_lifetime</span><span style="color: #339933;">,</span> <span style="color: #000088;">$default_cache_max_lifetime</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #000088;">$factor</span> <span style="color: #339933;">=</span> <span style="color:#800080;">0.1</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//典型设置为10%</span>
&nbsp;
        <span style="color: #000088;">$heuristic</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//  启发式 默认为0</span>
&nbsp;
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$Max_Age_value_set</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>   <span style="color: #666666; font-style: italic;">// 优先级一为 Max_Age</span>
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$Max_Age_value</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #b1b100;">elseif</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$Expires_value_set</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>  <span style="color: #666666; font-style: italic;">//   优先级二为Expires</span>
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$Expires_value</span> <span style="color: #339933;">-</span> <span style="color: #000088;">$Date_value</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #b1b100;">elseif</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$Last_Modified_value_set</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #666666; font-style: italic;">//  优先级三为Last_Modified</span>
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>int<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$factor</span> <span style="color: #339933;">*</span> <span style="color: #990000;">max</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> <span style="color: #000088;">$Last_Modified_value</span> <span style="color: #339933;">-</span> <span style="color: #000088;">$Date_value</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000088;">$heuristic</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//  启发式</span>
        <span style="color: #009900;">&#125;</span><span style="color: #b1b100;">else</span><span style="color: #009900;">&#123;</span>  
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$default_cache_min_lifetime</span><span style="color: #339933;">;</span>
            <span style="color: #000088;">$heuristic</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//  启发式</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$heuristic</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">&gt;</span> <span style="color: #000088;">$default_cache_max_lifetime</span> ? <span style="color: #000088;">$default_cache_max_lifetime</span> <span style="color: #339933;">:</span> <span style="color: #000088;">$freshness_lifetime</span><span style="color: #339933;">;</span>
            <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$freshness_lifetime</span> <span style="color: #339933;">&lt;</span> <span style="color: #000088;">$default_cache_min_lifetime</span> ? <span style="color: #000088;">$default_cache_min_lifetime</span> <span style="color: #339933;">:</span> <span style="color: #000088;">$freshness_lifetime</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #b1b100;">return</span> <span style="color: #000088;">$freshness_lifetime</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>计算响应是否过期非常简单： response_is_fresh = (server_freshness_limit() &gt;  current_age)</p>
<p>以此为《HTTP权威指南》第七章读书笔记。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/12/http-cache-algorithm/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>锁机制之MySQL表锁</title>
		<link>https://www.phppan.com/2012/11/mysql-table-lock/</link>
		<comments>https://www.phppan.com/2012/11/mysql-table-lock/#comments</comments>
		<pubDate>Mon, 05 Nov 2012 00:23:29 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[锁机制]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1753</guid>
		<description><![CDATA[如何保证在被并发访问时数据的一致性、完整性和有效性，是数据库关注的核心问题。数据库的锁机制就是为了解决这个问题 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>如何保证在被并发访问时数据的一致性、完整性和有效性，是数据库关注的核心问题。数据库的锁机制就是为了解决这个问题而出现的。锁机制在一定程度上将对共享资源的并发访问有序化，从而保证数据的一致完整性。锁机制的好坏直接影响到数据的并发处理能力和性能。一个好的锁机制的实现是一个数据的核心竞争力之一。</p>
<p>我们知道在MySQL中存在表级锁、页级锁和行级锁，其中MySQL默认实现了表级锁定。其它锁机制在不同的存储引擎中实现，这也是MySQL特点之一：针对特定的应用场景可以使用当前合适的存储引擎。先不论各种存储引擎和锁机制的优劣，这里只是说说他们各自的特点和实现。</p>
<p>MyISAM存储引擎作为曾经的默认存储引擎，其使用的锁机制是MySQL提供的默认表级锁定。虽然它没有实现自己的锁机制，但是在默认表级锁的基础上，增加了并发插入的特性。并发插入与系统参数concurrent_insert相关，concurrent_insert有三个值：</p>
<ul>
<li>concurrent_insert=0 关闭并发写入</li>
<li>concurrent_insert=1 (默认)在没有空数据块的MyISAM表中启用并行插入</li>
<li>concurrent_insert=2  为所有MyISAM表启用并行插入。如果表有空记录或正被另一线程使用，新行将插入到表的最后。如果表未使用，MySQL将进行普通读锁定并将新行插入空记录。</li>
</ul>
<p>此参数与MyISAM存储引擎的数据存储方式相关：常规情况下，MyISAM的新数据都会被附加到数据文件的结尾，当做了一些DELETE操作之后，数据文件就不再是连续的，形象一点来说，就是数据文件里出现了很多hole，此时再插入新数据时，按缺省设置会先看这些hole的大小是否可以容纳下新数据，如果可以，则直接把新数据保存到hole里，反之，则把新数据保存到数据文件的结尾。之所以这样做是为了减少数据文件的大小，降低文件碎片的产生。</p>
<p>如果我们使用concurrent_insert=2（通常也推荐这样做），这样会产生较多的文件碎片，为此，我们需要在设置这个参数值的同时，定期对数据表进行OPTIMIZE  TABLE操作。此操作可以去除删除操作后留下的数据文件碎片，减小文件尺寸，加快未来的读写操作。但是，在OPTIMIZE  TABLE运行过程中，MySQL会锁表。</p>
<p>MySQL的表锁有两种模式：表共享读锁（Table Read Lock）和表独占写锁（Table Write  Lock）。共享锁和独占锁在锁机制中是一种非常普通的实现方式。  MyISAM在执行查询语句前，会自动给涉及的所有表加读锁，在执行更新操作（DDL）前，会自动给相关的表加写锁。  MySQL的读写锁（mysys/thr_lock.c）是通过4个队列来维护的，他们分别是：</p>
<ul>
<li>当前读锁队列（lock-&gt;read）： 存储当前持有读锁所有线程相关信息，按获取锁的时间排序</li>
<li>读锁等待队列（lock-&gt;read_wait）：存储正在等待读锁锁定资源的线程相关信息</li>
<li>当前写锁队列（lock-&gt;write）：存储当前持有写锁所有线程相关信息，按获取锁的时间排序</li>
<li>写锁等待队列（lock-&gt;write_wait）：存储正在等待写锁锁定资源的线程相关信息</li>
</ul>
<p>对于读锁，当请求的资源没有加写锁或在写锁等待队列中没有更高优先级的写锁定在等待。读锁是共享锁，不会阻塞其他进程对同一资源的读请求，但会阻塞对同一资源的写请求。只有当读锁释放后，才会执行其它进程的写操作。</p>
<p>对于写锁，当请求的资源在当前写锁队列、写锁等待队列或当前读锁队列，进入等待写锁队列；写锁会阻塞其他进程对同一资源的读和写操作，只有当写锁释放后，才会执行其它进程的读写操作。</p>
<p>表锁是MySQL数据库中加锁粒度最大的一种锁，除此之外，MySQL还有页级锁和行锁。表锁的执行开销小，加锁速度快，不会出现死锁，但是其加锁的粒度大，发生锁冲突的概率非常高，从而导致并发度低。可以考虑使用主从结构解决并发度低的问题。</p>
<p>参考资料</p>
<p>http://www.zhaokunyao.com/archives/206</p>
<p>http://dev.mysql.com/doc/refman/5.1/zh/database-administration.html</p>
<p>《MySQL性能调优与架构设计》 &#8211; 简朝阳</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/11/mysql-table-lock/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在线修改MySQL大表的表结构</title>
		<link>https://www.phppan.com/2012/05/online-schema-change/</link>
		<comments>https://www.phppan.com/2012/05/online-schema-change/#comments</comments>
		<pubDate>Thu, 10 May 2012 00:57:51 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[OSC]]></category>
		<category><![CDATA[大数据量]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1674</guid>
		<description><![CDATA[问题描述 由于某个临时需求，需要给在线MySQL的某个超过千万的表增加一个字段。此表在设计之时完全按照需求实现 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>问题描述</p>
<p>由于某个临时需求，需要给在线MySQL的某个超过千万的表增加一个字段。此表在设计之时完全按照需求实现，并没有多余的保留字段。</p>
<p>我们知道在MySQL中如果要执行ALTER TABLE操作，MySQL会通过制作原来表的一个临时副本来工作。对于表结构的修改在副本上施行，然后将新表替换原始表，此时会产生锁表，用户可以从原始表读取数据，而用户的更新和写入操作都会被lock，待新表准备好后写入新表。<br />
这对于在线的数据量较大的表来说是绝对无法容忍的，并且由于这种在线操作时间会很长，此时如果show processlist，会发现有若干的MySQL进程处于lock状态，当这种进程太多超过单台服务器允许的MySQL进程数，其它进程可能会被拒绝连接。</p>
<p>有哪些方案可以处理这个问题呢？<br />
<br />
<strong>方案1、直接ALTER TABLE</strong><br />
这个方案只能说这仅仅是一种方案，在某些非实时在线或数据量较小时有较好的表现。<br />
<br />
<strong>方案２、模拟数据库修改表结构的操作，在非数据库层实现整个过程。</strong></p>
<ol>
<li>实现业务中对于数据的读写分离</li>
<li>创建一个已经按需求修改好结构的新表</li>
<li>修改业务逻辑，将读操作指向旧表，将写操作指向新表。如果读旧表没有，再读新表，并将旧的数据写入到新表，当然这一步写入操作我们可以不用，我们可以在后台做一个定时任务将旧数据同步到新表。</li>
</ol>
<p>这种方案有一个较大的缺点，需要业务逻辑层配合实现数据的迁移，对于业务逻辑有修改，并且如果有多台机器的话，需要一台一台的修改，较费时间，但是对于MySQL的两种主要存储引擎都适用。</p>
<p> <br />
<strong>方案３、facebook <a href="http://bazaar.launchpad.net/~mysqlatfacebook/mysqlatfacebook/tools/files/head:/osc/">online schema change</a></strong><br />
facebook的OSC在整体流程上与方案2没有较大的区别，只是它在这里引入了触发器，从而不需要修改业务逻辑，在数据库层就实现了新数据的两个表的同步问题。其大概步骤如下：</p>
<ol>
<li>按需求创建新表</li>
<li>针对原始表创建触发器</li>
<li>对于原始表的更新操作都会被触发器更新到新表中</li>
<li>把原始表中的数据复制到新表中</li>
<li>将新表替换旧表</li>
</ol>
<p>fb的osc方案从数据库层解决了方案2的问题，但是它仅支持InnoDB存储引擎。</p>
<p> <br />
<strong>方案４、换一个思路，保留字段。</strong><br />
假设一切可以从头再来，我们也许可以加多一些冗余字段，各个类型都加一些，备用。只是，回不去了！<br />
<br />
<strong>方案5、再换一个思路，增加扩展表。</strong><br />
我们不在原有的表的基础上修改了，以增加扩展表的方式，将新字段的数据写入到扩展表中，修改业务逻辑，这些字段从新表中读取。<br />
<a href="http://liuzhiqiangruc.iteye.com/">志强同学</a>说这是典型的维表结构设计。<br />
暂时解决了问题，如果这些字段后续使用频率高的话，可能会有对后期维护或业务有一定的影响。<br />
<br />
<strong>后记</strong><br />
基于现有的需求，只是需要记录新的字段，所以采用了扩展表的方案。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/05/online-schema-change/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从PHP的自动测试想到的</title>
		<link>https://www.phppan.com/2012/04/php-test-and-monitor/</link>
		<comments>https://www.phppan.com/2012/04/php-test-and-monitor/#comments</comments>
		<pubDate>Mon, 23 Apr 2012 00:59:52 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[PHP源码阅读笔记]]></category>
		<category><![CDATA[监控框架]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1663</guid>
		<description><![CDATA[从PHP的自动测试想到的 昨日，因TIPI项目而阅读了PHP的自动测试实现相关代码。于此，有些许感想，记录如下 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>从PHP的自动测试想到的</p>
<p>昨日，因TIPI项目而阅读了PHP的自动测试实现相关代码。于此，有些许感想，记录如下。</p>
<p>1、用自己测试自己，制定测试过程规范。<br />
PHP的测试环境是用PHP实现的，这不得不说是一个创新之举。相对于编译型语言，作为动态语言的PHP在应对变化上有着不少的优势，而测试本来就是一个变化是非较多的地儿。其实用到了PHP的地方只是这个框架的控制器，即源码根目录下run-tests.php文件。作为控制器，它实现了整个测试过程的控制。以一个测试过程为例，总体上分为三个部分：准备、运行和显示结果。准备活动包括测试所必须的环境变量的读取与设置，对测试参数的解析，测试脚本名的解析，各种输出文件的准备 解析测试脚本中的各个段落等；运行活动包括构造测试语句，执行测试语句，得到实际运行结果；显示结果活动包括测试后的结果比对及输出，相关记录记录以及总的测试报告显示。</p>
<p>这个控制器就是PHP自动测试的规范，所有的逻辑都在这一个脚本文件中，在一个时间点上，这是一个不变的过程。对于测试中变化的内容如测试环境，测试输入数据、需要验证的内容以及针对不同输入和不同测试点应该得到的预期结果，这些都存储在PHPT文件中，以不同的标记作为段分开。这些文件按模块划分，一个用例就是一个文件，与将用例写成代码相比，优势不仅仅在于工作量，更多的是在于它的扩展性、可读性和可维护性。</p>
<p>2、简单监控框架</p>
<p>先确认我们这个监控框架的需求什么。现在我们要的是一个可以监控数据是否正常，数据的状态是否符合业务逻辑，并将监控的结果发给相关负责人。从这个简单的需求出发，我们可以发现这里变化的是监控的内容，而不变的是整个监控的流程：查询特定的数据源，根据具体业务确认数据的正确性和合理性，并将结果发送给相关责任人。</p>
<p>对于不变的因素，我们可以以公共模块的方式在代码中实现，如果汇报结果的形式有不同的分类和权限控制的话，我们可以将这些配置放到数据库，当然，我们还是需要在代码中实现这些汇报的方式。</p>
<p>对于变化的因素，我们可以学习PHP的测试过程，以某些特定的规则定义一个一个的监控，我们可以称之为监控用例。在用例中定义名称、输入、过程和预期结果。比如，我们可以定义&#8211;SQL&#8211;字段做数据源。当然，这些内容我们可以分散存储，也可以集中存储在数据库。</p>
<p>这样一种以测试的方式实现监控过程，也许可以试试。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/04/php-test-and-monitor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Form表单的enctype属性和method属性</title>
		<link>https://www.phppan.com/2012/04/form-enctype-method/</link>
		<comments>https://www.phppan.com/2012/04/form-enctype-method/#comments</comments>
		<pubDate>Thu, 19 Apr 2012 00:43:39 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[HTML]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1651</guid>
		<description><![CDATA[在WEB开发过程中，Form表单元素是一个使用频率非常高的控件，对于这样一个控件，也许我们并没有认真关注过。今 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>在WEB开发过程中，Form表单元素是一个使用频率非常高的控件，对于这样一个控件，也许我们并没有认真关注过。今天我们来解读它的enctype属性和method属性。</p>
<h3><strong>enctype 属性</strong></h3>
<p>enctype属性规定在发送到服务器之前应该如何对表单数据进行编码。它的编码方式有三种：</p>
<ul>
<li>
application/x-www-form-urlencoded编码是以name=value键值对为基础，以&#038;连接；<br />
此为默认值。如果method属性为GET，则编码后的字符串会接到url的后面（其实用其它编码方式，GET的效果也是一样的）。<br />
如果method属性为POST，则编码后的字符串会被封装到HTTP协议的请求实体中，然后发送到服务器。
</li>
<li>
text/plain编码是以name=value键值对为基础，以\r\n连接；如果服务端的程序是PHP的话，使用此编码，如果method为GET，一切和其它编码一样，如果method为POST，则无论是$_GET、$_POST还是$_REQUEST都无法获取数据，为什么呢？因为PHP对于POST方法处理方法中根本就没有针对这种编码的处理函数。当然，我们可以通过php://input或$HTTP_RAW_POST_DATA获取POST过来的原始值。
</li>
<li>
multipart/form-data编码，这是最为特殊的编码；以其Content-Type后面的boundary为分隔符，将各个控件的值包含的请求实体中。
</li>
</ul>
<p>对于POST请求，一般来说用默认的application/x-www-form-urlencoded就可以了。但是如果有文件控件（type=file）的话，就要用到multipart/form-data了。浏览器会把整个表单以控件为单位分割，并为每个部分加上 Content-Disposition(form-data或者file)，Content-Type(默认为text/plain，且没有显示),name(控件的name)等信息，并加上分割符(boundary)。</p>
<h3><strong>method 属性</strong></h3>
<p>Form的method属性支持POST和GET方法。默认为GET提交。<br />
GET方法用于信息获取，而且应该是安全的和幂等的。所谓安全指该操作用于获取信息而非修改信息。换句话说，GET请求一般不应产生副作用。相当于SQL中的SELECT操作。所谓幂等指对同一URL的多个请求应该返回同样的结果。比如sina网中点击某一个新闻页面，不同的时候返回应该是同一篇文章，如果后台有修改这条新闻，用户所看到的内容不同，但是我们还是会认为这是幂等的。</p>
<p>POST方法表示可能修改变服务器上的资源的请求。这里的修改包括在服务器上增加资源，修改已有资源或者其它修改类型的操作。</p>
<p>虽然method只支持这两个方法，但是HTTP协议还定义了一些其它的方法：<br />
比如PUT方法，它表示完全替换或更新一个已经存在的资源或创建一个新的资源。PUT与POST的差别是这是一个完整的修改，不存在只修改部分。比如DELETE，它表示删除一个资源。</p>
<p>只是，在实际应用中，为了图方便，我们经常使用GET方法实现修改操作，因为这样我们不需要创建表单，如此而已。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/04/form-enctype-method/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
