<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-405440534643619078</id><updated>2012-01-30T20:08:38.914-08:00</updated><category term='Scala'/><category term='Project Management'/><category term='erlang'/><category term='Database'/><category term='Linux'/><category term='web service'/><category term='其它'/><category term='SSL'/><category term='benchmark'/><category term='Tips'/><category term='JSON'/><category term='Java'/><category term='SOA'/><title type='text'>Daniel's Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>41</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-6710045473412100254</id><published>2011-04-28T03:20:00.000-07:00</published><updated>2011-04-28T03:53:01.614-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='benchmark'/><title type='text'>A quick map access benchmark</title><content type='html'>I got a 10K entries big hash map and have 1k threads to traverse the map.&lt;br /&gt;&lt;br /&gt;Single thread&lt;br /&gt;Erlang ETS 3+ seconds&lt;br /&gt;Java HashMap 0.18 seconds&lt;br /&gt;Java ConcurrentHashMap 0.41 seconds&lt;br /&gt;High-scale lib NonBlockingHashMap 0.43 seconds&lt;br /&gt;&lt;br /&gt;1000 threads&lt;br /&gt;Erlang ETS 2+ seconds&lt;br /&gt;Java HashMap N/A (not thread-safe)&lt;br /&gt;Java ConcurrentHashMap 0.56 seconds&lt;br /&gt;High-scale lib NonBlockingHashMap 0.51 seconds&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-6710045473412100254?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/6710045473412100254/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=6710045473412100254' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/6710045473412100254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/6710045473412100254'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2011/04/quick-map-access-benchmark.html' title='A quick map access benchmark'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-6246086622728573815</id><published>2011-04-13T07:26:00.000-07:00</published><updated>2011-04-13T08:48:25.915-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='JSON'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>json java libraries benchmark - jsonlib, jackson, gson</title><content type='html'>&lt;p&gt;Because I don't parse huge json string so I only test object binding which is much more convenient. For the same reason, I did not have jettison or gson-streaming in this test because they are much faster with the trade-off of lower level of encapsulation and OOP.&lt;br /&gt;&lt;br /&gt;For gson and jackson, the instance of Gson and ObjectMapper can be reused. Jackson performs very good because its ObjectMapper caches class mapping meta data. Jackson will be slow if you don't reuse ObjectMapper, it's designed for reuse.&lt;br /&gt;&lt;br /&gt;The scala built-in parser is extremely slow (100 X slower!) and lift-json is the defacto lib for scala, which is about 15% slower than jackson.&lt;br /&gt;&lt;/p&gt;&lt;table style="margin: 0px; padding: 0px; border: medium dashed; width: 360px; height: 184px;"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;-&lt;/td&gt;&lt;br /&gt;&lt;td&gt;Bean to JSON&lt;/td&gt;&lt;br /&gt;&lt;td&gt;JSON to Bean&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Jsonlib 2.4&lt;/td&gt;&lt;br /&gt;&lt;td&gt;2058&lt;/td&gt;&lt;br /&gt;&lt;td&gt;3055&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Gson 1.7&lt;/td&gt;&lt;br /&gt;&lt;td&gt;1481&lt;/td&gt;&lt;br /&gt;&lt;td&gt;1472&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Jackson 1.7.6&lt;/td&gt;&lt;br /&gt;&lt;td&gt;694&lt;/td&gt;&lt;br /&gt;&lt;td&gt;667&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Test env: jdk6 on Mac OS X 10.6.7&lt;br /&gt;unit: millseconds&lt;br /&gt;&lt;br /&gt;Conclusion: Jackson is the best solution for server side because of its cache. Another win of jackson is that it has zero dependency, jsonlib sucks a lot in both performance and dependency. Gson is also very fast with zero dependency, without cache, it's ad-hoc usage is the best, it's best for less heavy use of json, like android device or web start applet (170K only).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-6246086622728573815?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/6246086622728573815/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=6246086622728573815' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/6246086622728573815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/6246086622728573815'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2011/04/json-java-libraries-benchmark-jsonlib.html' title='json java libraries benchmark - jsonlib, jackson, gson'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-4242760369812656489</id><published>2010-05-07T03:02:00.000-07:00</published><updated>2010-05-07T03:03:08.014-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>Fix eclipse corrupt index</title><content type='html'>When your eclipse index is corrupt, delete these files:&lt;br /&gt;&lt;br /&gt;C:\pogo\p4\.metadata\.plugins\org.eclipse.jdt.core\savedIndexNames.txt&lt;br /&gt;C:\pogo\p4\.metadata\.plugins\org.eclipse.jdt.core\*.index&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-4242760369812656489?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/4242760369812656489/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=4242760369812656489' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4242760369812656489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4242760369812656489'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2010/05/fix-eclipse-corrupt-index.html' title='Fix eclipse corrupt index'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-3646287976661748334</id><published>2010-04-19T22:54:00.000-07:00</published><updated>2010-04-19T22:58:36.489-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>利用Dropbox同步Squirrel SQL Client配置</title><content type='html'>安装dropbox并设置好同步, 找到这个文件&lt;br /&gt;C:\Documents and Settings\&lt;用户名&gt;\.squirrel-sql&lt;br /&gt;先备份之, 然后复制一份到你的dropbox共享目录,比如我的是"C:\Documents and Settings\danielwu\Desktop\My Dropbox\Config Files".&lt;br /&gt;&lt;br /&gt;在Squirrel SQL client的启动脚本里最后启动java的地方加上一个参数&lt;br /&gt;start "Squirrel SQL Client" ...... -Duser.home="C:\Documents and Settings\danielwu\Desktop\My Dropbox\Config Files" ......&lt;br /&gt;&lt;br /&gt;启动Squirrel SQL看看是否正常, 如果一切正常那么,把所有需要同步的电脑都这样改脚本, 等dropbox同步好了,就可以了.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-3646287976661748334?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/3646287976661748334/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=3646287976661748334' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/3646287976661748334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/3646287976661748334'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2010/04/dropboxsquirrel-sql-client.html' title='利用Dropbox同步Squirrel SQL Client配置'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-5185088694639147031</id><published>2010-04-04T19:21:00.000-07:00</published><updated>2010-04-04T19:24:24.759-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>卸载微软拼音2007输入法</title><content type='html'>实在搞不懂为什么微软从Office2000开始总要搭配这个病毒一样的输入法。&lt;br /&gt;&lt;br /&gt;1. MsiExec.exe /X{90120000-0028-0804-0000-0000000FF1CE} &lt;br /&gt;2. 控制面板里删掉mspy 3&lt;br /&gt;3. 注册表删掉Windows/Current_Version/IME下的东西&lt;br /&gt;重启&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-5185088694639147031?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/5185088694639147031/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=5185088694639147031' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5185088694639147031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5185088694639147031'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2010/04/2007.html' title='卸载微软拼音2007输入法'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-7377047016105861226</id><published>2010-03-22T22:16:00.001-07:00</published><updated>2010-03-22T22:34:15.619-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>多台电脑自动同步FoxyProxy黑白名单设置</title><content type='html'>安装dropbox并设置好同步, 找到这个文件&lt;br /&gt;C:\Documents and Settings\&lt;用户名&gt;\Application Data\Mozilla\Firefox\Profiles\&lt;字符若干&gt;.default\foxyproxy.xml&lt;br /&gt;先备份之.&lt;br /&gt;&lt;br /&gt;在FoxProxy 的options --&gt; global setting --&gt; miscellaneous --&gt; settings storage location &lt;br /&gt;&lt;br /&gt;所有电脑都弄好之后, 关闭所有的firefox, 把备份的文件再复制回dropbox, 结束. 一劳永逸, 不需要导入导出了, 只要一台电脑改过你的白名单, 其他电脑自动更改.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-7377047016105861226?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/7377047016105861226/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=7377047016105861226' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/7377047016105861226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/7377047016105861226'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2010/03/foxyproxy.html' title='多台电脑自动同步FoxyProxy黑白名单设置'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-5986163447019094257</id><published>2009-06-23T18:06:00.001-07:00</published><updated>2009-06-23T18:10:07.705-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Database'/><title type='text'>Oracle EM Console 更改hostname后无法启动</title><content type='html'>Oracle EM Console 比较弱智，改了机器名或者IP经常就不能正常启动，用这个命令行来重新配置EM Console吧&lt;br /&gt;&lt;br /&gt;emca -config dbcontrol db&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-5986163447019094257?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/5986163447019094257/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=5986163447019094257' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5986163447019094257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5986163447019094257'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/06/oracle-em-console-hostname.html' title='Oracle EM Console 更改hostname后无法启动'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-1738255999736160157</id><published>2009-05-14T01:13:00.000-07:00</published><updated>2009-05-14T01:14:46.034-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Database'/><title type='text'>Oracle三种常用Join</title><content type='html'>The three most commonly used joins are Indexed Nested Loops, Hash Join, and Sort-Merge Join.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Indexed Nested Loops&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The Nested Loop join is an iterative join: for each row in the first (inner) row source, lookup matching rows in the second (outer) row source. If the nested lookup of the second row source performs a Unique or Range Index Scan, then we call this Indexed Nested Loops.&lt;br /&gt;&lt;br /&gt;Indexed Nested Loops is used primarily in low volume joins; it is efficient over small volumes and versatile enough to be used in a variety of situations. Although it is fully scalable, Indexed Nested Loops is inefficient over large data volumes.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Hash Join&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The hash join is used for high-volume equi-joins (joins with equals predicates). Oracle performs a single read of the smaller row source (call this T1) and builds a hash table in memory. The join key is used as the hash-key of the hash table. Then a single pass of the larger row source (call this T2) is performed, hashing the join key of each row to obtain an address in the hash table where it will find matching T1 rows.&lt;br /&gt;&lt;br /&gt;Provided T1 remains small enough to build the hash table in memory, T2 can be scaled up to any arbitrarily large volume without affecting throughput or exceeding temp space. If T1 cannot be hashed in memory, then a portion of the hash-table spills to disk. When the hash table is probed by T2, the rows with join keys that match those parts of the in-memory hash table are joined immediately; the rest are written to TEMP and joined in a second pass. The bigger T1 is, the smaller the proportion of the hash table that can fit in memory, and the larger the proportion of T2 that must be scanned twice. This slows the Hash Join down considerably and also makes the join non-scalable.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Sort-Merge&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A sort-merge join works by reading each row-source in the join separately; sorting both sets of results on the join column(s); then concurrently working through the two lists, joining the rows with matching keys. Sort-Merge is generally faster than Indexed Nested Loops but slower than Hash Join for equi-joins. It is used almost exclusively for non-equi joins (&gt;, &lt;, BETWEEN) and will occasionally be used when one of the row sources is pre-sorted (eg. a GROUP BY inline view)&lt;br /&gt;&lt;br /&gt;If both row sources are small then they may both be sorted in memory, however large sorts will spill to disk making then non-scalable.&lt;br /&gt;&lt;br /&gt;There is no way to make a Sort-Merge join scalable. The only other way to resolve a non-equijoin is to use Nested Loops, which is slower. As volumes increase, Sort-Merge will continue to out-perform Nested Loops, but will eventually run out of Temp space. The only solution is to extend TEMP, or convert the join to Nested Loops (and then wait).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-1738255999736160157?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/1738255999736160157/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=1738255999736160157' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/1738255999736160157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/1738255999736160157'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/05/oraclejoin.html' title='Oracle三种常用Join'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-5359227872999140182</id><published>2009-03-23T06:35:00.001-07:00</published><updated>2009-03-25T00:21:33.347-07:00</updated><title type='text'>Don't cache Singleton object in a serializable object</title><content type='html'>不要在可序列化对象中缓存Singleton&lt;br&gt;&lt;br&gt;如果你有一个对象其中某个字段保存了一个对Singleton的引用，那么这个对象在序列化读取后会导致在同一个虚拟机里Singleton对象有两个。比如下面的例子&lt;br&gt;&lt;br&gt;public class Test3 {&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public static void main(String[] args) throws IOException, ClassNotFoundException {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;System.out.println(ABC.getInstance());&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;ByteArrayOutputStream out = new ByteArrayOutputStream();&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;new ObjectOutputStream(out).writeObject(ABC.getInstance());&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;ABC abc = (ABC) in.readObject();&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;System.out.println(abc);&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br&gt;}&lt;br&gt;&lt;br&gt;class ABC implements Serializable {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;private ABC() {}&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;private static ABC instance = new ABC();&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public static ABC getInstance() {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;return instance;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br&gt;}&lt;br&gt;&lt;br&gt;你会发现，打印出的两个ABC的instance是不一样的。解决方法很简单，在ABC加入这个方法&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected Object readResolve() {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return instance;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;这样可以重置instance到这个虚拟机中的Singleton实例，还可以把引用instance的字段作为transient字段节省IO时间。&lt;br&gt;但是这不是最好的办法，最好就是，如果你知道ABC是Singleton，那么就永远不要把ABC赋值到你的对象成员变量里，getInstance()因为是static方法，编译器通常会做inline的，频繁调用不会导致频繁方法栈操作，所以缓存它意义不大。&lt;br&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-5359227872999140182?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/5359227872999140182/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=5359227872999140182' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5359227872999140182'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5359227872999140182'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/03/singleton.html' title='Don&apos;t cache Singleton object in a serializable object'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-217134584063734375</id><published>2009-03-22T23:47:00.001-07:00</published><updated>2009-03-25T00:22:04.169-07:00</updated><title type='text'>Literal Pitfall</title><content type='html'>我们过去经常使用这样的方式定义常量, 比如我最不喜欢的java.util.Calendar类里面定义月份有&lt;br&gt;public static final int &amp;nbsp;&amp;nbsp; &amp;nbsp;APRIL&amp;nbsp; =&amp;nbsp;&amp;nbsp; 3&lt;br&gt;public static final int &amp;nbsp;&amp;nbsp;&amp;nbsp; MAY &amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp; 4&lt;br&gt;public static final int &amp;nbsp;&amp;nbsp;&amp;nbsp; JUNE &amp;nbsp; =&amp;nbsp;&amp;nbsp; 5&lt;br&gt;...&lt;br&gt;&lt;br&gt;我相信很多人也是这样定义常量或者枚举型。其实这样会有一个很严重的问题，和编译器的行为有关系。VM Spec 2.17.4中描述类初始化的发生条件时提到ClassA的某个常量字段比如ClassA.MAX被访问的时候不会导致ClassA类被初始化。&lt;br&gt;&lt;br&gt;2.17.4 Initialization&lt;br&gt;........&lt;br&gt;A class or interface type T will be initialized immediately before one of the following occurs:&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; * T is a class and an instance of T is created.&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; * T is a class and a static method of T is invoked.&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; * A nonconstant static field of T is used or assigned. A constant field is one that is (explicitly or implicitly) both final and static, and that is initialized with the value of a compile-time constant expression. A reference to such a field must be resolved at compile time to a copy of the compile-time constant value, so uses of such a field never cause initialization. &lt;br&gt;&lt;br&gt;原因是如果ClassB引用ClassA.MAX，编译器会把ClassA.MAX的常量值复制到ClassB的常量池中。这样显然效率更高。&lt;br&gt;&lt;br&gt;我们做一个实验，有两个类&lt;br&gt;public class ConstClass {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public static final int TEST = 5;&lt;br&gt;}&lt;br&gt;&lt;br&gt;public class RefClass {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void main(String[] args) {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;System.out.println(ConstClass.TEST);&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br&gt;}&lt;br&gt;编译后，执行RefClass明显应该打印5，我们用javap看一下pesudo code:&lt;br&gt;public class RefClass extends java.lang.Object{&lt;br&gt;public RefClass();&lt;br&gt;&amp;nbsp; Code:&lt;br&gt;&amp;nbsp;&amp;nbsp; 0:&amp;nbsp;&amp;nbsp; aload_0&lt;br&gt;&amp;nbsp;&amp;nbsp; 1:&amp;nbsp;&amp;nbsp; invokespecial&amp;nbsp;&amp;nbsp; #1; //Method java/lang/Object."&amp;lt;init&amp;gt;":()V&lt;br&gt;&amp;nbsp;&amp;nbsp; 4:&amp;nbsp;&amp;nbsp; return&lt;br&gt;&lt;br&gt;public static void main(java.lang.String[]);&lt;br&gt;&amp;nbsp; Code:&lt;br&gt;&amp;nbsp;&amp;nbsp; 0:&amp;nbsp;&amp;nbsp; getstatic&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #2; //Field java/lang/System.out:Ljava/io/PrintStream;&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;b style="color: rgb(255, 0, 0);"&gt;3:&amp;nbsp;&amp;nbsp; iconst_5&lt;/b&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; 4:&amp;nbsp;&amp;nbsp; invokevirtual&amp;nbsp;&amp;nbsp; #3; //Method java/io/PrintStream.println:(I)V&lt;br&gt;&amp;nbsp;&amp;nbsp; 7:&amp;nbsp;&amp;nbsp; return&lt;br&gt;&lt;br&gt;}&lt;br&gt;&lt;br&gt;这里你会看到iconst_5，RefClass并没有让VM加载ConstClass,事实上，你删除ConstClass.class也没有关系。&lt;br&gt;问题来了，如果你这样定义常量或者枚举值，将来如果ClassA.MAX的值你需要更改，那么你必须重新编译所有引用过这个值的类！那些类需要重新编译，这是非常难预测的，尤其是被频繁使用的API.&lt;br&gt;&lt;br&gt;如何克服呢？有两种方式，一种是提供一个getTEST()来返回常量值，比如把刚才的类改成&lt;br&gt;public class ConstClass {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public static final int TEST = 5;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public static int getTEST() {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;return TEST;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br&gt;}&lt;br&gt;public class RefClass {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public static void main(String[] args) {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;System.out.println(ConstClass.getTEST());&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br&gt;}&lt;br&gt;由于getTEST()是static，编译器可能会inline他，因此效率不会太低&lt;br&gt;&lt;br&gt;还有一种方式，是jdk1.5之后提供的enum&lt;br&gt;让我们重新写这两个类&lt;br&gt;public enum ConstClass2 {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;TEST(5);&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;private int value;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;ConstClass2(int v) {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.value = v;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public int getValue() {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;return this.value;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br&gt;}&lt;br&gt;&lt;br&gt;public class RefClass2 {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public static void main(String[] args) {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;System.out.println(ConstClass2.TEST.getValue());&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br&gt;}&lt;br&gt;在用javap看看引用类&lt;br&gt;public class RefClass2 extends java.lang.Object{&lt;br&gt;public RefClass2();&lt;br&gt;&amp;nbsp; Code:&lt;br&gt;&amp;nbsp;&amp;nbsp; 0:&amp;nbsp;&amp;nbsp; aload_0&lt;br&gt;&amp;nbsp;&amp;nbsp; 1:&amp;nbsp;&amp;nbsp; invokespecial&amp;nbsp;&amp;nbsp; #1; //Method java/lang/Object."&amp;lt;init&amp;gt;":()V&lt;br&gt;&amp;nbsp;&amp;nbsp; 4:&amp;nbsp;&amp;nbsp; return&lt;br&gt;&lt;br&gt;public static void main(java.lang.String[]);&lt;br&gt;&amp;nbsp; Code:&lt;br&gt;&amp;nbsp;&amp;nbsp; 0:&amp;nbsp;&amp;nbsp; getstatic&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #2; //Field java/lang/System.out:Ljava/io/PrintStream;&lt;br&gt;&lt;b style="color: rgb(255, 0, 0);"&gt;&amp;nbsp;&amp;nbsp; 3:&amp;nbsp;&amp;nbsp; getstatic&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #3; //Field ConstClass2.TEST:LConstClass2;&lt;/b&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; 6:&amp;nbsp;&amp;nbsp; invokevirtual&amp;nbsp;&amp;nbsp; #4; //Method ConstClass2.getValue:()I&lt;br&gt;&amp;nbsp;&amp;nbsp; 9:&amp;nbsp;&amp;nbsp; invokevirtual&amp;nbsp;&amp;nbsp; #5; //Method java/io/PrintStream.println:(I)V&lt;br&gt;&amp;nbsp;&amp;nbsp; 12:&amp;nbsp; return&lt;br&gt;&lt;br&gt;}&lt;br&gt;你会发现这次5没有被复制到引用类的常量池，相反getstatic代替了iconst_，相当于ClassA.MAX会被解释成ClassA.getMAX()，这样效果其实和上面说的另外一种方法getTEST()是类似的。&lt;br&gt;&lt;br&gt;其实enum还有其他的好处，JDK guide中说 (http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html)&lt;br&gt;&lt;br&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Not typesafe&lt;/b&gt; - Since a season is just an &lt;code&gt;int&lt;/code&gt; you can pass in any other &lt;tt&gt;int&lt;/tt&gt; value where a season is required, or add two seasons together (which makes no sense).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;No namespace&lt;/b&gt; - You must prefix constants of an int enum with a string (in this case &lt;code&gt;SEASON_&lt;/code&gt;) to avoid collisions with other int enum types.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Brittleness&lt;/b&gt; - Because int enums are compile-time&lt;br /&gt;constants, they are compiled into clients that use them. If a new&lt;br /&gt;constant is added between two existing constants or the order is&lt;br /&gt;changed, clients must be recompiled. If they are not, they will still&lt;br /&gt;run, but their behavior will be undefined.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Printed values are uninformative&lt;/b&gt; - Because they are&lt;br /&gt;just ints, if you print one out all you get is a number, which tells&lt;br /&gt;you nothing about what it represents, or even what type it is.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;其中第三点就是本文描述的问题，另外typesafe也是个问题，比如你完全可以把Integer.MAX_VALUE 传给 Calendar.set(Integer.MAX_VALUE, somevalue)，另外没有命名空间而且打印出来也很不友好。&lt;br&gt;&lt;br&gt;所以，总之，还是enum吧？&lt;br&gt;&lt;br&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-217134584063734375?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/217134584063734375/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=217134584063734375' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/217134584063734375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/217134584063734375'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/03/blog-post.html' title='Literal Pitfall'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-2330847775921702672</id><published>2009-02-26T01:15:00.000-08:00</published><updated>2009-02-26T01:22:37.912-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Project Management'/><title type='text'>Software Compatibility with Model-driven development</title><content type='html'>&lt;iframe src='http://docs.google.com/EmbedSlideshow?docid=d82bt7r_95fgrsbccx' frameborder='0' width='410' height='342'&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-2330847775921702672?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/2330847775921702672/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=2330847775921702672' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/2330847775921702672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/2330847775921702672'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/02/software-compatibility-with-model.html' title='Software Compatibility with Model-driven development'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-4821236506984032232</id><published>2009-02-16T18:23:00.000-08:00</published><updated>2009-02-24T23:08:14.110-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>SetPoint Ctrl/Shift 粘滞的解决</title><content type='html'>我把罗技MX Revolution鼠标的查找键设置成为了快速关闭，但是后来发现关闭会发送Alt+F4到应用程序，对于多tab的程序，比如firefox, notepad++会关闭掉整个程序，而不是一个tab，所以我改成了Ctrl+F4，然后问题就来了，有时候Ctrl+F4关闭窗口的时候，Ctrl会一直被按下去，除非你再按两下Ctrl.&lt;br /&gt;&lt;br /&gt;解决方法一共三种，来源于http://www.mstar.net/users/rlowens/?n=SetPoint.StuckModifiers，最后一种最好用，如下&lt;br /&gt;   1. Set a button to Keys:Close (Alt+F4) in SetPoint&lt;br /&gt;   2. Close SetPoint from the System Tray&lt;br /&gt;   3. Edit your setting file (in notepad.exe or the like). Start-&gt;Run-&gt;\Application Data\Logitech\SetPoint and then edit the user.xml file in that folder (see below).&lt;br /&gt;   4. Restart c:\Program Files\Logitech\SetPoint\SetPoint.exe or reboot &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;For Ctrl+W, change:&lt;br /&gt;&lt;br /&gt;                     &lt; Handler Class="KeyStroke" &gt;&lt;br /&gt;                        &lt; Param KeyName="%{F4}"/ &gt;&lt;br /&gt;                     &lt; /Handler &gt;&lt;br /&gt;&lt;br /&gt;to:&lt;br /&gt;&lt;br /&gt;                     &lt; Handler Class="KeyStroke" &gt;&lt;br /&gt;                        &lt; Param KeyName="^w"/ &gt;&lt;br /&gt;                     &lt; /Handler &gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The modifiers are:&lt;br /&gt;&lt;br /&gt;^ ctrl&lt;br /&gt;+ shift&lt;br /&gt;* win&lt;br /&gt;% alt&lt;br /&gt;&lt;br /&gt;Some extended keys are:&lt;br /&gt;&lt;br /&gt;    * {PGDN}&lt;br /&gt;    * {PGUP}&lt;br /&gt;    * {TAB}&lt;br /&gt;    * {ESC}&lt;br /&gt;    * ~ (Enter)&lt;br /&gt;    * {BKSP}&lt;br /&gt;    * {RIGHT}&lt;br /&gt;    * {LEFT}&lt;br /&gt;    * {F1-F16} works (F17-F24 don't work, have to use the KeystrokeAssignment handler instead)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-4821236506984032232?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/4821236506984032232/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=4821236506984032232' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4821236506984032232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4821236506984032232'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/02/setpoint-ctrlshift.html' title='SetPoint Ctrl/Shift 粘滞的解决'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-9182709036406939359</id><published>2009-02-10T00:14:00.000-08:00</published><updated>2009-02-10T00:49:18.883-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Database'/><title type='text'>使用Oracle开发国际化的应用，字符长度问题</title><content type='html'>最早是在和IBM的一个项目做接口的时候，发现了一些在开发国际化系统时忽略的一些Oracle的问题。&lt;br /&gt;&lt;br /&gt;首先说一下字符长度的定义&lt;br /&gt;&lt;br /&gt;1. 对于ISO8859 和 ANSI/ASCII: 都是1个字符就是1 byte 但是 ANSI/ASCII是7 bits，所以有128个基本字符，而ISO8859是8 bits，而且有多个集合，因此ISO8859包含ANSI/ASCII，然后还提供了128个额外特殊字符，比如英镑符号&lt;br /&gt;2. 对于GBK/GB2312: 1个字符是2 bytes&lt;br /&gt;3. 对于UTF-8: 1个字符是3 bytes&lt;br /&gt;&lt;br /&gt;有了这个知识，你就明白了，其实在国际化的应用中定义字符的长度不是随意的。如果你随便定义了一个列是varchar2(30)，这个30是什么意思呢？你可能认为是30个字符，其实不是，是30 bytes!&lt;br /&gt;&lt;br /&gt;这样的话，如果Oracle默认编码是UTF8，如果你要对这个列插入一个字符串包含8个中文和6个英文，那么这个字符串是8*3+6=30个字节，你可以插入，但是如果这个字符串包含8个中文和7个英文，那么这个字符串是8*3+7=31个字节，你就无法插入了。这样的话，你在界面里很难控制字符串长度的校验。&lt;br /&gt;&lt;br /&gt;怎么解决呢？有三种办法&lt;br /&gt;&lt;br /&gt;1. 最好的办法就是用nvarchar2(30)，这个定义表示30个字符，编码方式只可以选utf8或者utf16&lt;br /&gt;2. 使用UTF8作为默认编码，创建表的时候指定varchar2(30 char)显式表示这是30个字符，不是30个byte&lt;br /&gt;3. 修改Oracle参数nls_length_semantics=CHAR，然后再创建表结构，这样的话varchar2(30)表示varcahr2(30 char).但是这个参数不影响已经创建的表，只能用于新建的表。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-9182709036406939359?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/9182709036406939359/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=9182709036406939359' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/9182709036406939359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/9182709036406939359'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/02/oracle.html' title='使用Oracle开发国际化的应用，字符长度问题'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-5064217269748764507</id><published>2009-02-09T21:26:00.000-08:00</published><updated>2009-02-09T21:27:39.623-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SOA'/><title type='text'>Jackbe CTO on Mashup</title><content type='html'>&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/CFbFNjXS5Qw&amp;amp;hl=en&amp;amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/CFbFNjXS5Qw&amp;amp;hl=en&amp;amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-5064217269748764507?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/5064217269748764507/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=5064217269748764507' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5064217269748764507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5064217269748764507'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/02/jackbe-cto-on-mashup.html' title='Jackbe CTO on Mashup'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-5953402136689203035</id><published>2009-01-18T08:09:00.001-08:00</published><updated>2009-01-18T08:10:47.142-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>用 PhantomReference 避免OutOfMemory</title><content type='html'>&lt;p&gt;&lt;/p&gt;&lt;p&gt;PhantomReference比Weak/Soft的引用强度都要低,PhantomReference.get()总是返回null,为什么呢？&lt;br /&gt;&lt;/p&gt;&lt;p&gt;其实PhantomReference而要是不可缺少的重要引用类型, 我们知道WeakReference在finalize方法调用或被gc清理之前会进入ReferenceQueue, 这时候没有任何strong reference引用对象，然后可以通过ReferenceQueue去做收尾工作。但是在finalize()方法，或者重新给这个对象一个引用使它reachable(从某个活动线程调用栈，或者静态变量可达), 使这个对象延长生命周期暂时不会被gc清理。而PhantomReference只会在对象被从内存中清除后才会进入队列，get()总是返回null (ReferenceQueue通知一个PhantomReference的时候，既然内存都已经物理释放了，当然也无法给你一个对象，所以get总是返回null也有这个原因), 所以你没有办法重新使这个对象再次reachable。PhantomReference只是提供了一种方式让你跟踪一个曾经产生过的对象，由此让你知道这个对象到底有没有被物理的清除。&lt;/p&gt;比如当你的applet程序需要处理一个非常大的图片的时候，你可能希望图片处理结束并且内存被释放之后再处理下一个图片。如果用PhantomReference来引用上一个图片对象，当ReferenceQueue通知你的时候，你就可以知道上一个内存对象已经被物理清除，你可以继续下一个大内存对象的处理了。这样就可以避免由于GC线程优先级低导致上一个大内存对象还没有释放下一个大内存对象又被创建，让OutOfMemoryError出现的概率低一些。&lt;br /&gt;&lt;p&gt;还有一个好处，就是PhantomReference比用finalize方法好的多，因为VM对finalize的处理不如PhantomReference简单可靠，只不过是你要写的代码稍微多一点点而已。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;Ethan有一个更完整的对四种reference的解释 http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-5953402136689203035?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/5953402136689203035/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=5953402136689203035' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5953402136689203035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5953402136689203035'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/01/phantomreference-outofmemory.html' title='用 PhantomReference 避免OutOfMemory'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-499729965206849233</id><published>2009-01-09T23:37:00.001-08:00</published><updated>2009-01-17T03:48:38.050-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>指定 Eclipse 代码模板的 @author</title><content type='html'>默认情况下新建一个java文件，eclipse产生的type comment是&lt;br /&gt;&lt;p&gt;/**&lt;br /&gt;* @author xxx&lt;br /&gt;*&lt;br /&gt;*/&lt;/p&gt;其中xxx是我的域帐号，怎样才能把这个换成我要的name呢？两个办法，一个比较笨，去改template，把type&lt;br /&gt;&lt;p&gt;comment里的${user}替换成你的name，另一个办法就是在你的eclipse启动脚本或者快捷方式里加入&lt;/p&gt;C:\Tools\eclipse\eclipse.exe -vmargs -Duser.name="Daniel Woo"&lt;br /&gt;&lt;br /&gt;如图：&lt;p&gt;&lt;/p&gt;&lt;div id="hqd3" style="padding: 1em 0pt; text-align: left;"&gt;&lt;img style="width: 365px; height: 503px;" src="http://docs.google.com/File?id=d82bt7r_88f27p9wgg_b" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;或者修改eclipse.ini在-vmargs后面添加黑体部分&lt;br /&gt;-vmargs&lt;br /&gt;-Dosgi.requiredJavaVersion=1.5&lt;br /&gt;-Xms40m&lt;br /&gt;-Xmx512m&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;-Duser.name=Daniel Woo&lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-499729965206849233?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/499729965206849233/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=499729965206849233' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/499729965206849233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/499729965206849233'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/01/eclipse-author.html' title='指定 Eclipse 代码模板的 @author'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-2788743773230274058</id><published>2009-01-06T07:55:00.000-08:00</published><updated>2009-01-18T07:00:04.935-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web service'/><title type='text'>JAX-RS看起来很简洁！</title><content type='html'>&lt;h2&gt;JSR 311 (JAX-RS)&lt;br /&gt;&lt;/h2&gt;                      &lt;p&gt;J2EE将支持Restful Web Service，届时Restlet, RestEasy估计都会提供兼容的方式。编写起来使用annotation非常简单清晰，比如这段代码&lt;/p&gt;&lt;h4&gt;&lt;code&gt;import javax.ws.rs.Path;&lt;br /&gt;import javax.ws.rs.Produces;&lt;br /&gt;import javax.ws.rs.GET;&lt;br /&gt;&lt;br /&gt;@Path("sample")&lt;br /&gt;public class SampleResource {&lt;br /&gt;   private String message;&lt;br /&gt;&lt;br /&gt;public SampleResource() {&lt;br /&gt;  this("Hello");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public SampleResource(String message) {&lt;br /&gt;     this.message = message;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@GET&lt;br /&gt;@Produces("text/html")&lt;br /&gt;public String getHTML() {&lt;br /&gt;     StringBuffer sb = new StringBuffer("");&lt;br /&gt;     sb.append(message);&lt;br /&gt;     sb.append("");&lt;br /&gt;  return sb.toString();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@GET&lt;br /&gt;@Produces("text/xml")&lt;br /&gt;public String getXML() {&lt;br /&gt;     StringBuffer sb = new StringBuffer("&lt;message&gt;");&lt;br /&gt;     sb.append(message);&lt;br /&gt;   sb.append("&lt;/message&gt;");&lt;br /&gt;     return sb.toString();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@GET&lt;br /&gt;@Produces("text/plain")&lt;br /&gt;public String getPlain() {&lt;br /&gt;     return message;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/h4&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-2788743773230274058?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/2788743773230274058/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=2788743773230274058' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/2788743773230274058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/2788743773230274058'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/01/jax-rs.html' title='JAX-RS看起来很简洁！'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-24616090516159453</id><published>2009-01-04T08:16:00.001-08:00</published><updated>2009-01-04T08:17:56.930-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Database'/><title type='text'>Oracle 的锁基础原理</title><content type='html'>&lt;p&gt;Oracle有以下类别的锁，本文主要只讨论DML的锁&lt;/p&gt;  &lt;p&gt;•DML locks (data locks)&lt;/p&gt;  &lt;p&gt;•DDL locks (dictionary locks) &lt;/p&gt;  &lt;p&gt;•Oracle Internal Locks/Latches&lt;/p&gt;  &lt;p&gt;•Oracle Distributed Locks&lt;/p&gt;  &lt;p&gt;•Oracle Parallell Cache Management Locks&lt;/p&gt;  &lt;p&gt;DML的锁是针对并发数据访问的，因此分行级别TX和表级别TM&lt;/p&gt;  &lt;h5&gt;两种经典锁&lt;/h5&gt;  &lt;p&gt;共享锁S表示其他事务可以读取这个资源但是任何事务都不可以更改这个资源，任何事物都不可以再加X锁。&lt;/p&gt;  &lt;p&gt;排他锁X表示其他事务可以读取资源，但只有当前事务可以更改这个资源，其他任何事物都不可以再加S或者X锁。&lt;/p&gt;  &lt;h5&gt;行级锁&lt;/h5&gt;  &lt;p&gt;也叫事务锁，简称TX锁。两种经典锁，行级别上面只支持X锁（排它锁），Oracle没有行级别S锁（共享锁）。所有DML操作一般自动生成行级锁,行级别锁理论上比表级别锁效率高，因为他的锁定范围更小更精确。&lt;/p&gt;  &lt;p&gt;Oracle当中一个记录被insert/update/delete/select for update之后，当前事务T1自动获得这行的X排他锁，另外一个事务T2如果要更改这条记录，另外一个事务T2会被阻塞，直到T1提交或者回滚。&lt;/p&gt;  &lt;p&gt;比如&lt;/p&gt;  &lt;table style="width: 421px; height: 140px;" border="1" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="40"&gt;步骤&lt;/td&gt;        &lt;td valign="top" width="131"&gt;T1&lt;/td&gt;        &lt;td valign="top" width="148"&gt;T2&lt;/td&gt;        &lt;td valign="top" width="141"&gt;备注&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="40"&gt;1&lt;/td&gt;        &lt;td valign="top" width="131"&gt;update row1&lt;/td&gt;        &lt;td valign="top" width="148"&gt; &lt;/td&gt;        &lt;td valign="top" width="141"&gt; &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="40"&gt;2&lt;/td&gt;        &lt;td valign="top" width="131"&gt; &lt;/td&gt;        &lt;td valign="top" width="148"&gt;update row1&lt;/td&gt;        &lt;td valign="top" width="141"&gt;T2被阻塞&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="40"&gt;3&lt;/td&gt;        &lt;td valign="top" width="131"&gt;commit&lt;/td&gt;        &lt;td valign="top" width="148"&gt; &lt;/td&gt;        &lt;td valign="top" width="141"&gt;T1提交，T2可以继续&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="40"&gt;4&lt;/td&gt;        &lt;td valign="top" width="131"&gt; &lt;/td&gt;        &lt;td valign="top" width="148"&gt;commit&lt;/td&gt;        &lt;td valign="top" width="141"&gt; &lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;如果在第一步之后，你执行select * from v$lock where type in ('TX','TM')，你就会看到有一个行级别锁TX&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_MvCA5zIFrxg/SWDgyNSfeAI/AAAAAAAAHwM/amcb14MXkGU/s1600-h/image%5B8%5D.png"&gt;&lt;img title="image" style="border: 0px none ; display: inline; width: 396px; height: 69px;" alt="image" src="http://lh3.ggpht.com/_MvCA5zIFrxg/SWDgzcZlUFI/AAAAAAAAHwQ/S_GlMEd6jiE/image_thumb%5B6%5D.png?imgmax=800" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;其中LMODE=3和REQUEST=0，其中LMODE是已经获得的锁模式，取值从0到6, 对于行级别锁其实唯一的值是6，其他值都是对表级别锁的。REQUEST表示要求的锁，如果一个事务所要求的锁需要等待另一个事务释放，那么REQUEST可以看到这个事务需要请求什么类型的锁。&lt;/p&gt;  &lt;table width="298" border="1" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="58"&gt;0&lt;/td&gt;        &lt;td valign="top" width="229"&gt;NO LOCK&lt;/td&gt;        &lt;td valign="top" width="10"&gt; &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="58"&gt;1&lt;/td&gt;        &lt;td valign="top" width="229"&gt;NULL&lt;/td&gt;        &lt;td valign="top" width="10"&gt; &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="58"&gt;2&lt;/td&gt;        &lt;td valign="top" width="229"&gt;RS (Row Share)&lt;/td&gt;        &lt;td valign="top" width="10"&gt; &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="58"&gt;3&lt;/td&gt;        &lt;td valign="top" width="229"&gt;RX (Row Exclusive)&lt;/td&gt;        &lt;td valign="top" width="10"&gt; &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="58"&gt;4&lt;/td&gt;        &lt;td valign="top" width="229"&gt;S (Share)&lt;/td&gt;        &lt;td valign="top" width="10"&gt; &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="58"&gt;5&lt;/td&gt;        &lt;td valign="top" width="229"&gt;SRX (Share + Row Exclusive)&lt;/td&gt;        &lt;td valign="top" width="10"&gt; &lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="58"&gt;6&lt;/td&gt;        &lt;td valign="top" width="229"&gt;X (Exclusive)&lt;/td&gt;        &lt;td valign="top" width="10"&gt; &lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;这里这个行级别锁TX已经获得了在行row1上的排他锁。&lt;/p&gt;  &lt;p&gt;如果在第二步执行之后，T2会被阻塞，你打开一个新的连接再一次执行select * from v$lock where type in ('TX','TM')，你就会看到多出来一个行级别锁TX&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_MvCA5zIFrxg/SWDg0O_A7KI/AAAAAAAAHwU/aeGPQFFsLOE/s1600-h/image%5B15%5D.png"&gt;&lt;img title="image" style="border: 0px none ; display: inline; width: 406px; height: 98px;" alt="image" src="http://lh3.ggpht.com/_MvCA5zIFrxg/SWDg19TvrEI/AAAAAAAAHwY/qYu9O3Ek9Kg/image_thumb%5B11%5D.png?imgmax=800" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;这个新多出来的行级别锁在row1上获得锁是0(无)，要求锁是6(X)，同时，刚才那个行级别锁的block标志位变成了1，表示有别的事务等待我释放这个锁。&lt;/p&gt;  &lt;p&gt;然后如果你提交T1，T2获得锁，可以继续下去，相应的只剩下了T2的row1上的锁，而且LMODE从0变成了6， REQUEST从6变成了0&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_MvCA5zIFrxg/SWDg2p8XwcI/AAAAAAAAHwc/BupW3d-ccR0/s1600-h/image%5B21%5D.png"&gt;&lt;img title="image" style="border: 0px none ; display: inline; width: 377px; height: 66px;" alt="image" src="http://lh6.ggpht.com/_MvCA5zIFrxg/SWDg4gZfEQI/AAAAAAAAHwg/RzSNiIDm6Cs/image_thumb%5B15%5D.png?imgmax=800" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;另外，这时候T1会自动在表级别也加一个锁，目的是防止事务过程中发生DDL操作。其中SID是session ID，可以通过V$session查看，ID1和ID2是rollback segment和transaction table entry.&lt;/p&gt;  &lt;h5&gt;表级锁&lt;/h5&gt;  &lt;p&gt;简称TM锁，可分为以下5种 &lt;/p&gt;  &lt;p&gt;&lt;b&gt;RS&lt;/b&gt;: row share，意向锁，表示表内部分行有S锁了，部分行不许更改了&lt;/p&gt;  &lt;p&gt;&lt;b&gt;RX&lt;/b&gt;: row exclusive ，意向锁，表示表内部分行有变更，有X锁了&lt;/p&gt;  &lt;p&gt;&lt;b&gt;S&lt;/b&gt;: share ，整个表有S锁，这个表不可以更改&lt;/p&gt;  &lt;p&gt;&lt;b&gt;SRX&lt;/b&gt;: share + row exclusive，只有一个事务可以获得SRX锁，其他事务可以查询但是不可以更改 &lt;/p&gt;  &lt;p&gt;&lt;b&gt;X&lt;/b&gt;: exclusive, 只有一个事务可以获得X表锁， 其他事务只能查询&lt;/p&gt;  &lt;p&gt;RS,RX比较特殊，他们是DML操作产生的常见表级锁，他们都只是意向锁，真正的加锁粒度还是在行级，相当于某些行被锁之后，用这两个意向锁锁一下所属的表，数据库会比较容易判断行级别的锁情况。 &lt;/p&gt;  &lt;p&gt;从刚才的例子中可以看到，TX总是伴随着一个LMODE=3的TM锁，即RX锁，只要表中有行有X锁，表就会有RX锁。RX表示事务已经更改了某些行，比如insert/update/delete，获得了某些行X锁。RX不会阻塞RX锁，除非行锁上有阻塞。刚才的例子，如果两个事务更改的是两个记录，那么就不会互相阻塞。&lt;/p&gt;  &lt;p&gt;如果执行了lock table test in row share mode你会看到LMODE=2(Row Share)的一个表级别锁。RS级别的锁只阻止X级别表锁，它不阻止RS, S等。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_MvCA5zIFrxg/SWDg5W2XqmI/AAAAAAAAHwk/iun9sFXDNSU/s1600-h/image%5B31%5D.png"&gt;&lt;img title="image" style="border: 0px none ; display: inline; width: 387px; height: 49px;" alt="image" src="http://lh3.ggpht.com/_MvCA5zIFrxg/SWDg6dllKyI/AAAAAAAAHwo/zEoROYt66g4/image_thumb%5B23%5D.png?imgmax=800" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;详细的锁之间的关系如下(相信大多数人就算是理解了也要要想半天的)&lt;a href="http://lh3.ggpht.com/_MvCA5zIFrxg/SWDg7jXcioI/AAAAAAAAHws/ZB2SSIbxw4A/s1600-h/image%5B35%5D.png"&gt;&lt;img title="image" style="border: 0px none ; display: inline;" alt="image" src="http://lh5.ggpht.com/_MvCA5zIFrxg/SWDg9tmCEhI/AAAAAAAAHww/nhCZMk9FtMk/image_thumb%5B25%5D.png?imgmax=800" width="395" border="0" height="220" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-24616090516159453?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/24616090516159453/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=24616090516159453' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/24616090516159453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/24616090516159453'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/01/oracle.html' title='Oracle 的锁基础原理'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_MvCA5zIFrxg/SWDgzcZlUFI/AAAAAAAAHwQ/S_GlMEd6jiE/s72-c/image_thumb%5B6%5D.png?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-4384857531255391332</id><published>2009-01-04T05:34:00.001-08:00</published><updated>2009-01-04T08:47:26.276-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Database'/><title type='text'>SQL Server, Oracle 和 MySQL 事务隔离等级实现差别</title><content type='html'>&lt;h6&gt;&lt;/h6&gt;  &lt;h4&gt;基本概念&lt;/h4&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;脏读：包含未提交数据的读。例如，事务1 更改了某行。事务2 在事务1 提交更改之前读取已更改的行。如果事务1 回滚更改，则事务2 便读取了逻辑上从未存在过的行。&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;不可重复读取：当某个事务不止一次读取同一行，并且一个单独的事务在两次（或多次）读取之间修改该行时。因为在同一个事务内的多次读取之间修改了该行，所以每次读取都生成不同值，从而引发不一致问题。&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;幻像：通过一个任务，在以前由另一个尚未提交其事务的任务读取的行的范围中插入新行或删除现有行。带有未提交事务的任务由于该范围中行数的更改而无法重复其原始读取。如果某个连接设置其事务隔离级别为可串行，则 SQL Server 使用键范围锁定以防止幻像。&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;SQL-92 定义了下列四种隔离级别&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;   &lt;table border="1" cellpadding="0"&gt;&lt;tbody&gt;       &lt;tr&gt;         &lt;td valign="top" width="30%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;隔离级别&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="21%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;脏读&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="31%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;不可重复读取&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="18%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;幻像&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;       &lt;/tr&gt;        &lt;tr&gt;         &lt;td valign="top" width="30%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;未提交读&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="21%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;是&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="31%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;是&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="18%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;是&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;       &lt;/tr&gt;        &lt;tr&gt;         &lt;td valign="top" width="30%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;提交读&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="21%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;否&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="31%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;是&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="18%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;是&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;       &lt;/tr&gt;        &lt;tr&gt;         &lt;td valign="top" width="30%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;可重复读&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="21%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;否&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="31%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;否&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="18%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;是&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;       &lt;/tr&gt;        &lt;tr&gt;         &lt;td valign="top" width="30%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;可串行读&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="21%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;否&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="31%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;否&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;          &lt;td valign="top" width="18%"&gt;           &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;否&lt;/span&gt;&lt;/p&gt;         &lt;/td&gt;       &lt;/tr&gt;     &lt;/tbody&gt;&lt;/table&gt; &lt;/p&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;我们使用三个主流数据库测试，分别是Oracle 10g, SQL Server 2005和MySQL 5.1 (InnoDB only)&lt;/span&gt;&lt;/p&gt;  &lt;h4&gt;测试幻读&lt;/h4&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;首先创建一个表test，并插入三条测试数据&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style=";font-family:微软雅黑;font-size:78%;"  &gt;CREATE TABLE test&lt;br /&gt;(&lt;br /&gt;c1  INTEGER,&lt;br /&gt;c2  INTEGER&lt;br /&gt;); &lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style=";font-family:微软雅黑;font-size:78%;"  &gt;insert into test values(1,1);&lt;br /&gt;insert into test values(2,2);&lt;br /&gt;insert into test values(3,3);&lt;br /&gt;commit;&lt;/span&gt;&lt;/p&gt;  &lt;h5&gt;&lt;strong&gt;SQL Server消除幻读测试&lt;/strong&gt;&lt;/h5&gt;  &lt;table style="width: 438px; height: 344px;" border="1" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td  valign="top" width="42" style="font-family:verdana;"&gt;&lt;span style="font-size:85%;"&gt;Steps&lt;/span&gt;&lt;/td&gt;        &lt;td  valign="top" width="132" style="font-family:verdana;"&gt;&lt;span style="font-size:85%;"&gt;T1&lt;/span&gt;&lt;/td&gt;        &lt;td  valign="top" width="126" style="font-family:verdana;"&gt;&lt;span style="font-size:85%;"&gt;T2&lt;/span&gt;&lt;/td&gt;        &lt;td  valign="top" width="148" style="font-family:verdana;"&gt;&lt;span style="font-size:85%;"&gt;备注&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;1&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;set transaction isolation level serializable;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;begin transaction;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;           &lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt; &lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1事务启动，设置隔离等级到Serializable， 然后查询一下test，会看到3条记录&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;begin transaction;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;             &lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;     &lt;br /&gt;&lt;/span&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;insert into test values(100, 100);       &lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T2启动，插入一行并提交，这时候SQL Server会阻塞这个插入, 因为T1加了范围锁&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;3&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test; &lt;br /&gt;commit;&lt;/span&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1第二次查询应该看到和第一次一样的三行，然后提交&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;commit;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/code&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;只有T1提交之后，T2才可以插入&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;br /&gt;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;从sp_lock输出来看，在第一步之后，T1对test表加了S锁，所以T2可以选test的数据但是无法更改&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Oracle&lt;/strong&gt;&lt;strong&gt;消除幻读测试&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;  &lt;table style="width: 431px; height: 360px;" border="1" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="font-size:85%;"&gt;Steps&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;span style="font-size:85%;"&gt;T1&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;span style="font-size:85%;"&gt;T2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;span style="font-size:85%;"&gt;备注&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;1&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;set transaction isolation level serializable;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;begin transaction;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;           &lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt; &lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1事务启动，设置隔离等级到Serializable， 然后查询一下test，会看到3条记录&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;begin transaction;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;             &lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;     &lt;br /&gt;&lt;/span&gt;&lt;code&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="font-family:微软雅黑;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;insert into test values(100, 100);           &lt;br /&gt;commit;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T2启动，插入一行并提交，这时候Oracle不会阻塞这个插入，然后T2成功提交&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;3&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test; &lt;br /&gt;commit;&lt;/span&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1第二次查询仍然看到和第一次一样的三行，然后提交&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;4&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;&lt;/span&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;新事物开始，这时候T1才会看到刚才T2插入的记录&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;br /&gt;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;从V$lock来看，整个过程Oracle没有加锁，Oracle使用了SCN从回滚段重建出来当初时刻的数据提供一个snapshot 来保证T1不受T2的影响。&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;MySQL消除幻读测试&lt;/strong&gt;&lt;/p&gt;  &lt;table style="width: 420px; height: 344px;" border="1" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="font-size:85%;"&gt;Steps&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="font-size:85%;"&gt;T1&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;span style="font-size:85%;"&gt;T2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="font-size:85%;"&gt;备注&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;1&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;set transaction isolation level serializable;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;start transaction;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;           &lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt; &lt;/p&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1事务启动，设置隔离等级到Serializable， 然后查询一下test，会看到3条记录&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;start transaction;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;             &lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;     &lt;br /&gt;&lt;/span&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;insert into test values(100, 100);       &lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T2启动，插入一行并提交，这时候MySQL会阻塞这个插入, 因为T1加了范围锁&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;3&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test; &lt;br /&gt;commit;&lt;/span&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1第二次查询应该看到和第一次一样的三行，然后提交&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;commit;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/code&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;只有T1提交之后，T2才可以插入&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;br /&gt;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;和SQL Server一样的处理方式，但是MySQL也有snapshot, 效果和Oracle一样，语法是START TRANSACTION WITH CONSISTENT SNAPSHOT;从这个角度来说MySQL实现还是不错的，比SQL Server好一些。&lt;/span&gt;&lt;/p&gt;  &lt;h4&gt;测试不可重复读&lt;/h4&gt;  &lt;p&gt;&lt;strong&gt;SQL Server消除不可重复测试&lt;/strong&gt;&lt;/p&gt;  &lt;table style="width: 423px; height: 344px;" border="1" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="font-size:85%;"&gt;Steps&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="font-size:85%;"&gt;T1&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;span style="font-size:85%;"&gt;T2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="font-size:85%;"&gt;备注&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;1&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;set transaction isolation level repeatable read;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;begin transaction;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;           &lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt; &lt;/p&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1事务启动，设置隔离等级到Serializable， 然后查询一下test，会看到3条记录&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;begin transaction;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;             &lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;     &lt;br /&gt;&lt;/span&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;update test set c2=200 where c1=2;       &lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T2启动，更改一行，这时候SQL Server会阻塞这个更改, 因为T1在记录行上加了X和U锁 (排他更新锁)&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;3&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test; &lt;br /&gt;commit;&lt;/span&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1第二次查询应该看到和第一次一样记录，然后提交&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;commit;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/code&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;只有T1提交之后，T2才可以更新&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;br /&gt;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;&lt;strong&gt;Oracle消除不可重复测试&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style=";font-family:微软雅黑;font-size:78%;"  &gt;Oracle不支持REPEATABLE READ这个隔离等级，我们需要用更高的Serializable级别来测试&lt;/span&gt;&lt;/p&gt;  &lt;table style="width: 433px; height: 360px;" border="1" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="font-size:85%;"&gt;Steps&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;span style="font-size:85%;"&gt;T1&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;span style="font-size:85%;"&gt;T2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;span style="font-size:85%;"&gt;备注&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;1&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;set transaction isolation level serializable;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;begin transaction;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;           &lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt; &lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1事务启动，设置隔离等级到Serializable， 然后查询一下test，会看到3条记录&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;begin transaction;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;             &lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;     &lt;br /&gt;&lt;/span&gt;&lt;code&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="font-family:微软雅黑;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;update test set c2=200 where c1=2;           &lt;br /&gt;commit;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T2启动，更改一行并提交，这时候Oracle不会阻塞这个更新，然后T2成功提交&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;3&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test; &lt;br /&gt;commit;&lt;/span&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1第二次查询仍然看到和第一次一样的记录，然后提交&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;4&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;&lt;/span&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;新事物开始，这时候T1才会看到刚才T2更改的记录&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="142"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="116"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="152"&gt;&lt;br /&gt;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;从V$lock来看，整个过程Oracle没有加锁，Oracle使用了SCN从回滚段重建出来当初时刻的数据提供一个snapshot 来保证T1不受T2的影响。&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;MySQL消除不可重复测试&lt;/strong&gt;&lt;/p&gt;  &lt;table style="width: 422px; height: 322px;" border="1" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="font-size:85%;"&gt;Steps&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="font-size:85%;"&gt;T1&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;span style="font-size:85%;"&gt;T2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="font-size:85%;"&gt;备注&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;1&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;set transaction isolation level serializable;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;start transaction;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;              &lt;p&gt;&lt;code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;           &lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt; &lt;/p&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1事务启动，设置隔离等级到Serializable， 然后查询一下test，会看到3条记录&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;2&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;code&gt;&lt;code&gt;             &lt;/code&gt;&lt;/code&gt;&lt;p&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;start transaction;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;             &lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test;     &lt;br /&gt;&lt;/span&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;update test set c2=200 where c1=2;       &lt;br /&gt;commit;&lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T2启动，更改一行并提交，这时候MySQL不会阻塞这个更新.&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;3&lt;/span&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;select * from test; &lt;br /&gt;commit;&lt;/span&gt;&lt;/code&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;T1第二次查询应该看到和第一次一样的记录&lt;/span&gt;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="42"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="132"&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;&lt;/span&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="126"&gt;&lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="148"&gt;&lt;br /&gt;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;对于不可重复读，MySQL和Oracle一样采取了类似快照的方式，有点出乎意料，这点上MySQL实现比SQL Server要好一些。&lt;/span&gt;&lt;/p&gt;  &lt;h4&gt;测试脏读&lt;/h4&gt;  &lt;p&gt;&lt;span style="color: rgb(0, 0, 0);font-family:微软雅黑;font-size:78%;"  &gt;Read Committed可以消除脏读，由于比较简单，各个数据库实现差别不大，不会加特殊的锁，不再详细测试。这也是我们大多数情况下的数据库默认事务隔离等级。&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;   &lt;/p&gt;&lt;h4&gt;总结&lt;/h4&gt; &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;span style=";font-family:微软雅黑;font-size:78%;"  &gt;事务隔离等级不同的数据库实现有差别，有时候必须要清楚的了解之间的差别才能避免应用程序在多个数据库上兼容性和稳定性。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=";font-family:微软雅黑;font-size:78%;"  &gt;对于REPEATABLE READS，SQL Server设定隔离等级到REPEATABLE READ，对所有select过的数据加锁，阻止其他事务更新数据。但是其他事务可以插入幻读记录。而Oracle不直接支持这个等级，必须设定更高的SERIALIZABLE，Oracle使用SCN和Rollback segment重构snapshot，MySQL也是使用类似的snapshot，因次理论上讲SQL Server的这个隔离级别上并发性能比较差。&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=";font-family:微软雅黑;font-size:78%;"  &gt;对于PHANTOM幻读，三种数据库都是设定到SERIALIZABLE等级，但是SQL Server是通过加范围锁阻塞其他事务的插入和更新，Oracle使用snapshot, &lt;/span&gt;&lt;span style=";font-family:微软雅黑;font-size:78%;"  &gt;MySQL两种方式都支持。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=";font-family:微软雅黑;font-size:78%;"  &gt;另外，由于SERIALIZABLE级别上，SQL Server使用的是范围锁，所以其他数据无法插入或者更新，而Oracle不会阻塞其他事务更新数据，Oracle假设大多数情况下多个事务不会更新同一条记录，但是如果其他更新的记录和当前事务碰巧修改了同一条记录，Oracel会通过乐观锁发现这种情况，并报错臭名昭著的ORA-8177 Cannot serialize access for this transaction. 所以Oracle的Serializable虽然性能高，但是不可以用于长时间的事务或者频繁的OLTP系统。如果有这样的需要，必须通过lock table实现。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=";font-family:微软雅黑;font-size:78%;"  &gt;尽管这几个数据库都实现了ACID但是各有千秋，实现跨数据库的应用的时候需要小心。还有就是，不管数据库本身事务隔离等级和锁的实现效率差别如何，关键还是良好的架构，不好的架构往往在单节点低压力测试的时候速度很快但是在高并发的多处理器或者集群上横向伸缩的时候性能下降很快，Facebook或者LiveJournal用一大堆MySQL照样跑的很好， 不比昂贵的Oracle RAC差，架构决定伸缩性，而不应该迷信数据库本身。&lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-4384857531255391332?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/4384857531255391332/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=4384857531255391332' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4384857531255391332'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4384857531255391332'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2009/01/sql-server-oracle-mysql.html' title='SQL Server, Oracle 和 MySQL 事务隔离等级实现差别'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-2467502017286471443</id><published>2008-12-27T07:11:00.000-08:00</published><updated>2008-12-27T07:41:15.509-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Database'/><title type='text'>Hibernate 的乐观锁@Version和 MySQL 的两个小问题</title><content type='html'>MySQL的设计者一直没有支持精度到毫秒或者纳秒的Timestamp类型，过去一直只能支持到秒。这虽然不太方便但是也凑合着能用，比如我通过程序代码插入long int也可以。但是Hibernate的实体如果用了@Version在MySQL一个时间字段上做乐观锁，就有问题了。Hibernate比较实体对象是否改变过，是根据@version字段的值，由于MySQL的DATETIME类型不能包含毫秒，纳秒，所以比较的时候会有问题，导致Hibernate认为实体Version不相等，抛HibernateOptimisticLockingFailureException给你。&lt;br /&gt;&lt;br /&gt;MySQL 6 才会支持毫秒，所以目前你只能用INT或者BIGINT来保存Version了。&lt;br /&gt;但是就算是你用了INT你可能仍然遇到别的错误&lt;br /&gt;Caused by: java.lang.NullPointerException&lt;br /&gt;        at org.hibernate.type.IntegerType.next(IntegerType.java:59)&lt;br /&gt;        at org.hibernate.engine.Versioning.increment(Versioning.java:108)&lt;br /&gt;        at org.hibernate.event.def.DefaultFlushEntityEventListener.getNextVersion(DefaultFlushEntityEventListener.java:365)&lt;br /&gt;        at org.hibernate.event.def.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:257)&lt;br /&gt;        at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:128)&lt;br /&gt;&lt;br /&gt;这个错误一看就头大了吧，Hibernate的源代码是这样的&lt;br /&gt; public Object next(Object current, SessionImplementor session) {&lt;br /&gt;  return new Integer( ( (Integer) current ).intValue() + 1 );&lt;br /&gt; }&lt;br /&gt;初步分析原因是前面事件通知的时候传入的当前version是空的，具体看这里 http://opensource.atlassian.com/projects/hibernate/browse/HHH-3030&lt;br /&gt;&lt;br /&gt;最简单的解决办法是给@version字段在数据库里设置一个默认值，比如0&lt;br /&gt;&lt;br /&gt;Opensource意味着你不能发现bug的时候打电话对售后大喊大叫，没办法，要么自己写一个hotfix，要么等官方fix吧。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-2467502017286471443?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/2467502017286471443/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=2467502017286471443' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/2467502017286471443'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/2467502017286471443'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/12/hibernate-version-mysql.html' title='Hibernate 的乐观锁@Version和 MySQL 的两个小问题'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-1265011895787046831</id><published>2008-11-27T19:10:00.000-08:00</published><updated>2008-11-27T19:18:21.810-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Swing 的 Validate, Invalidate, Revalidate</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Validate/Invalidate/Revalidate in Swing&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Class Hierarchy&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Component.invalidate():&lt;/span&gt;&lt;br /&gt;Invalidates this component. This component and all parents above it are marked as needing to be laid out.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_MvCA5zIFrxg/SS9hY7E3D9I/AAAAAAAAHuU/tGXLe3oUO5o/s1600-h/1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 217px;" src="http://4.bp.blogspot.com/_MvCA5zIFrxg/SS9hY7E3D9I/AAAAAAAAHuU/tGXLe3oUO5o/s320/1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5273540769476972498" /&gt;&lt;/a&gt; &lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Component.validate()&lt;/span&gt;&lt;br /&gt;Ensures that this component has a valid layout. This method is primarily intended to operate on instances of Container&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_MvCA5zIFrxg/SS9hjpPCmKI/AAAAAAAAHuc/E_iGIQP6rIw/s1600-h/1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 202px;" src="http://2.bp.blogspot.com/_MvCA5zIFrxg/SS9hjpPCmKI/AAAAAAAAHuc/E_iGIQP6rIw/s320/1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5273540953666394274" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_MvCA5zIFrxg/SS9hrXtl-6I/AAAAAAAAHuk/s16LuJRABxk/s1600-h/1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 184px;" src="http://4.bp.blogspot.com/_MvCA5zIFrxg/SS9hrXtl-6I/AAAAAAAAHuk/s16LuJRABxk/s320/1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5273541086401657762" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Container.validate()&lt;/span&gt;&lt;br /&gt;Validates this container and all of its subcomponents. &lt;br /&gt;The validate method is used to cause a container to lay out its subcomponents again. It should be invoked when this container's subcomponents are modified (added to or removed from the container, or layout-related information changed) after the container has been displayed. &lt;br /&gt;Set valid=true and call Container.validateTree().&lt;br /&gt;Container.validateTree() will call all its sub containers’ validateTree() recursively, so all sub components’ Component.validate() method will be called.&lt;br /&gt; &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_MvCA5zIFrxg/SS9iEmwJgJI/AAAAAAAAHus/8WYqns2XKBc/s1600-h/1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 304px;" src="http://1.bp.blogspot.com/_MvCA5zIFrxg/SS9iEmwJgJI/AAAAAAAAHus/8WYqns2XKBc/s320/1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5273541519935635602" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Container.validateTree()&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_MvCA5zIFrxg/SS9iOIaln0I/AAAAAAAAHu0/a5yNREja-wU/s1600-h/1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 309px;" src="http://3.bp.blogspot.com/_MvCA5zIFrxg/SS9iOIaln0I/AAAAAAAAHu0/a5yNREja-wU/s320/1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5273541683590831938" /&gt;&lt;/a&gt;&lt;br /&gt; &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_MvCA5zIFrxg/SS9iWSob3oI/AAAAAAAAHu8/OUtKI9CAZi0/s1600-h/1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 297px; height: 204px;" src="http://2.bp.blogspot.com/_MvCA5zIFrxg/SS9iWSob3oI/AAAAAAAAHu8/OUtKI9CAZi0/s320/1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5273541823772221058" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Container.invalidate()&lt;/span&gt;&lt;br /&gt;Basically do the same thing as super class Component.invalidate except for notifying the layout manager the changes, since the major difference between Component and Container is that Container can contains objects with layout manager.&lt;br /&gt;LayoutManager2.invalidate() will discard the cached size information about the layout, so next time the layout will be re-calculated.&lt;br /&gt; &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_MvCA5zIFrxg/SS9im24FGWI/AAAAAAAAHvE/aneMYdb2bzs/s1600-h/1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 94px;" src="http://2.bp.blogspot.com/_MvCA5zIFrxg/SS9im24FGWI/AAAAAAAAHvE/aneMYdb2bzs/s320/1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5273542108379421026" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Conclusion on validate/invalidate&lt;/span&gt;&lt;br /&gt;Invalidate() causes the component hierarchy to be marked as needing to be laid out again, and the validate() causes that to be done. It may be expensive, but is a way of getting the peers to recalculate size and to do what is needed to bring the display up to date. It has limitations:  it doesn't cause an immediate screen update when invoked from an event handler, where paintImmediately() does.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;JComponent.revalidate()&lt;/span&gt;&lt;br /&gt;Supports deferred automatic layout. &lt;br /&gt;Calls invalidate and then adds this component's validateRoot to a list of components that need to be validated. Validation will occur after all currently pending events have been dispatched. In other words after this method is called, the first validateRoot (if any) found when walking up the containment hierarchy of this component will be validated. By default, JRootPane, JScrollPane, and JTextField return true from isValidateRoot. &lt;br /&gt; &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_MvCA5zIFrxg/SS9ivQplpmI/AAAAAAAAHvM/4qV-bI-kHFo/s1600-h/1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 207px;" src="http://4.bp.blogspot.com/_MvCA5zIFrxg/SS9ivQplpmI/AAAAAAAAHvM/4qV-bI-kHFo/s320/1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5273542252736915042" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Conclusion on revalidate&lt;/span&gt;&lt;br /&gt;This method will automatically be called on this component when a property value changes such that size, location, or internal layout of this component has been affected. This automatic updating differs from the AWT because programs generally no longer need to invoke validate to get the contents of the GUI to update. Because RepaintManger.addInvalidComponent() will validate them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-1265011895787046831?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/1265011895787046831/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=1265011895787046831' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/1265011895787046831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/1265011895787046831'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/11/swing-validate-invalidate-revalidate.html' title='Swing 的 Validate, Invalidate, Revalidate'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_MvCA5zIFrxg/SS9hY7E3D9I/AAAAAAAAHuU/tGXLe3oUO5o/s72-c/1.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-7883382746686117733</id><published>2008-11-24T21:21:00.000-08:00</published><updated>2008-11-26T18:13:12.586-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSL'/><title type='text'>OpenSSL转换PEM(Base64 DER)格式到PFX(PKCS12)格式</title><content type='html'>Convert PEM (Base64 DER) key pair into PFX (PKCS12)&lt;br /&gt;&lt;br /&gt;C:\&gt;openssl pkcs12 -export -in ca.cer -inkey ca.pem -out ca.pfx&lt;br /&gt;Loading 'screen' into random state - done&lt;br /&gt;Enter pass phrase for ca.pem:&lt;br /&gt;Enter Export Password:&lt;br /&gt;Verifying - Enter Export Password:&lt;br /&gt;&lt;br /&gt;签名试一下&lt;br /&gt;C:\&gt;signtool sign  /f ca.pfx /p password test.js&lt;br /&gt;Done Adding Additional Store&lt;br /&gt;Successfully signed: test.js&lt;br /&gt;&lt;br /&gt;导入到JKS格式试一下&lt;br /&gt;C:\Temp&gt;keytool -importkeystore -srckeystore ca.pfx -srcstoretype PKCS12 -destkeystore ca.jks&lt;br /&gt;Enter destination keystore password:&lt;br /&gt;Re-enter new password:&lt;br /&gt;Enter source keystore password:&lt;br /&gt;Entry for alias 1 successfully imported.&lt;br /&gt;Import command completed:  1 entries successfully imported, 0 entries failed or cancelled&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-7883382746686117733?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/7883382746686117733/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=7883382746686117733' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/7883382746686117733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/7883382746686117733'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/11/opensslpembase64-derpfxpkcs12.html' title='OpenSSL转换PEM(Base64 DER)格式到PFX(PKCS12)格式'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-45901305090598670</id><published>2008-11-13T07:17:00.000-08:00</published><updated>2008-11-13T07:22:19.591-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>添加JarFinder.com按钮到google bar</title><content type='html'>如果你的项目没有Maven2那么classNotFound可能经常会遇到，推荐一个网站jarfinder.com&lt;br /&gt;&lt;br /&gt;你可以把JarFinder作为一个google bar的search按钮。只要随便添加一个google按钮，然后选择编辑，use the advanced editor (关于google bar 的教程请访问 http://toolbar.google.com/buttons/apis)&lt;br /&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;custombuttons xmlns="http://toolbar.google.com/custombuttons/"&amp;gt;&lt;br /&gt;  &amp;lt;button&amp;gt;&lt;br /&gt;    &amp;lt;search method="get"&amp;gt;http://www.jarfinder.com/index.php/java/search/~{query}~&amp;lt;/search&amp;gt;&lt;br /&gt;    &amp;lt;site&amp;gt;http://www.jarfinder.com&amp;lt;/site&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;JarFinder&amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;icon type="image/x-icon" mode="base64"&amp;gt;&lt;br /&gt;iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAAK/INwWK6QAAABl0RVh0&lt;br /&gt;U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAaHSURBVEhLdZULUFTnGYYPN7Xaqk2T1kmn&lt;br /&gt;ztTpxIypGKgm02hjRDPVDDadXGqqsZXmAqJogEQNUoGCjYpiFBI02gIhXI2AAUyCARHkIiKSEhRY&lt;br /&gt;ri6wsO4FlsvCwvL0OwvWxNgz885/zn/Oed/v+n9OyNXV1aWMjY0pzs7Oyr3XBIoyZFMUV2eUh384&lt;br /&gt;qtgnFKV7cLqC4qTMmqYozk7f+0URSgfmz5+vKP39/Xh6egqNgqur6//g5Owqe5NY+pBC0BMKl997&lt;br /&gt;hK/Cf8VWD4XHfyIaU+8Vp7v/qRwq17Rp02hvb0exWCx4eHg4Nu/FdiFKXKvQmv4C6D7D0HoO+0A5&lt;br /&gt;dGfRnPJHktYpBHgquN3nXycnp7sCjy1Z6iB/UDxfvUAhN2A2I2lLsbccFk97BPWCr6nMCpG1SlAh&lt;br /&gt;aBD0Ym+OxpK6jNJ3ZvP8IoVf/OiOoa4i0IFi7rfw+vrf8PaTCtejH8H21Z8ZqQnD1vMpgzfiMF3e&lt;br /&gt;h6EsHMPF3RQEeaLN9kObsxVd/g7Z20N/bQyjnekMVIYwmP8yLccWsH+VwlMLXGhpu4ViNFtID1zE&lt;br /&gt;cEMaWIol4zFMtEdj00xirOUgupp4BrXxGKMWYqnex8CVvQxeDcUqhlhrBV+HM37zH0y0/BO7sQC7&lt;br /&gt;toyw9dNpam6dFPjX9sUMF3iL54Fw60vQ54P2kIgdZqwtjCtFCdTXZsInT8u79+Wbg9AWIdgn3xwX&lt;br /&gt;fAimIjDWwLV34Owcgte40tgsSVYFPtrmzmBlEFRugy/Wwvm1dFfEoyk/RUVBMqWFaRQXxDGU/Sw0&lt;br /&gt;7ZSUvCaJPi3pyZHnD+A/B6BgNWTPl3UZ9kteBD07QzxQBUz9nPBzZ+D63yWP+7BXBlB15l2KEgK5&lt;br /&gt;cDqQ3I9CKUzci+FGKrQfE+vjoK9Wci0Gfb5SjHkC8hZDoXhX8nso92a8fB07V/9gSsDczwd+Sxio&lt;br /&gt;PyLWxKAvjyAvMYIvEkIoTAjCkiVWZzwIR6R30p6BxpOQ7Ab57lAkAsVroHSdED8PV16UEL3CeM3L&lt;br /&gt;BHjNFIE2FIMIxL75ONbOJBiQRHdGU5oaTNLhAD497o/2zHNMZIiF6Q+Jxb8VgVD48kkhFuHLz00R&lt;br /&gt;S58IMddfhW98sH3zF7Y/M2tKwGTmtO+jlH4Wyt49vrx/9AAZGakkJacS9d4hoqKiOHUilvA929m5&lt;br /&gt;aSX1WS9B7SopCLG4SiyuVok3QZ0P3HgdNNuwNfqydeWUwIB08tu7JOal16i6WkN+Xi7ZaYnkfhLL&lt;br /&gt;obBg4mOPUlZRRaOmFasNTnxcSEyIVFzDn4RwiyRcSBt8hTgAWqUKtbsY7QjC9+nZkx6YzGbOns1S&lt;br /&gt;zzx8Xt2Al/vDeC90wvuXCh5y3rhMHQMzpk/naMwRtF0GQgI3Up65lq6KzUIoVdX6FnTskvu90txh&lt;br /&gt;jOpCeWPFHJrEKMVkMpGSkoLNZuP27ds0NjahabjBzbpqrldXc+3qNerq6miWpuns7ObixWLS09Io&lt;br /&gt;LSkhLjaGqoK/gUEqUBcmPRIlFXaQEeN+Xlsx965AamoqIyMj9Pb2CvSYzH0MDVuxWkcYHh6mr68f&lt;br /&gt;g8FER0cn5eVlmCRvw/K+tcPEqeN+Uhz7wRwtJ4E04ciHWG8fw2f5A9/1YHR0lJ6eHvR6vRD2OYhV&lt;br /&gt;0aGhIcwSxt5eg3igo0QsDw4OJiIinNraJk7F+8OY9Mew9MeolDCJWPXx/PWp+wio5GqYBgYGUAUn&lt;br /&gt;JiYcqyqg1xtkMOkoLCwiMjKS+voGfLZs41ZbJExIV9v/LeQfC1IY609my4qfotG0TOZADdG9HqjW&lt;br /&gt;q3uqJ98WKC0tIykpmY0bNzFkOSqE0uEOqIWSLvFP5ub5N3hxyRyaW+WoUAXS09MdSdbpdI4wGY1G&lt;br /&gt;1EmnDiN1NRiMsq+XEHVx4UIxu3e/KcLxQpgtOI/dlkTj50GcO7COLct+zOJZCnNnutHaJgIqgSqg&lt;br /&gt;XgaDAa1W60i2eq8KqSFTn7u7Vei4dKmC+NNSMZQw1HWEwoPeHHhpIYsecGHmtyabs4urFIUMHJVo&lt;br /&gt;/fo/cPJkgkygNrG010FqlkpSk62uRqNB9kw0NrUQd/wEkQFreHfVz/j1jO+P2Ttj183NbVJAtXLe&lt;br /&gt;vHm4uLjg7u6Jl9caNm/ejL+/Pzt2vIWvrx8bNrzC8uW/Y9FjS/j53GnfsfR+s/zOnkajQRkfH5fm&lt;br /&gt;uSjdfFbOoAzSpInUkKn3mZmZjlV9VvfPnMkkKzuHc7l55OXnk/9/kJeXR05OjvSRlf8CR6xc66yG&lt;br /&gt;aUcAAAAASUVORK5CYII=&lt;br /&gt;    &amp;lt;/icon&amp;gt;&lt;br /&gt;    &amp;lt;description&amp;gt;JarFinder&amp;lt;/description&amp;gt;&lt;br /&gt;&amp;lt;/button&amp;gt;&lt;br /&gt;&amp;lt;/custombuttons&amp;gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-45901305090598670?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/45901305090598670/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=45901305090598670' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/45901305090598670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/45901305090598670'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/11/jarfindercomgoogle-bar.html' title='添加JarFinder.com按钮到google bar'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-5320689722871766374</id><published>2008-11-11T18:06:00.001-08:00</published><updated>2008-11-11T18:34:04.867-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>免费杀毒软件AVG</title><content type='html'>最近被公司的Symantec Endpoint Protection折磨的实在不行了，在资源管理器里每打开一个文件夹都要延迟0.2秒左右，而卸载掉之后几乎没有任何延迟。对于普通用户来说这其实无所谓，但是对于我这样的开发人员，或者需要用QTab在资源管理器里经常大量文件操作的人来说，这种延迟实在是无法忍受，就算你禁止了SEP仍然速度非常慢。更气人的是，live Update的时候机器就像中毒一样，CPU两个核心占有率都保持50%以上，百般无奈，决定删之。回想当年Norton时代，Anti-Virus的运行效率多么令人敬佩，现在实在是失望。&lt;br /&gt;找了一下免费的防病毒软件，综合对比了一下，觉得AVG的Basic版本比较适合我，免费而且轻量。&lt;br /&gt;安装的时候我没有选择Link Scanner, E-Mail Scanner, Plugin for office, 因为我从来不轻易打开链接或者附件的，我需要的仅仅是文件系统实时保护。&lt;br /&gt;&lt;br /&gt;装上之后感受了一下，资源管理器果然没有了延迟问题！估计查杀病毒的能力和功能肯定是不如SEP，但是对于我来说，够了。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-5320689722871766374?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/5320689722871766374/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=5320689722871766374' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5320689722871766374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5320689722871766374'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/11/avg.html' title='免费杀毒软件AVG'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-4128086134812646375</id><published>2008-10-27T19:21:00.000-07:00</published><updated>2008-11-02T23:46:05.818-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web service'/><title type='text'>Mashups</title><content type='html'>非常浅显易懂而且有趣的video&lt;br /&gt;Restful Web Service, SOA, Mashups确实是大势所趋&lt;br /&gt;&lt;br /&gt;&lt;a style="left: 0px ! important; top: 0px ! important;" title="Click here to block this object with Adblock Plus" class="abp-objtab-011832107185760155 visible" href="http://www.youtube.com/v/3kDnbhKb2ow&amp;amp;hl=en&amp;amp;fs=1"&gt;&lt;/a&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/3kDnbhKb2ow&amp;amp;hl=en&amp;amp;fs=1"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;embed src="http://www.youtube.com/v/3kDnbhKb2ow&amp;amp;hl=en&amp;amp;fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-4128086134812646375?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/4128086134812646375/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=4128086134812646375' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4128086134812646375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4128086134812646375'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/10/mashups.html' title='Mashups'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-3795267453045242687</id><published>2008-10-14T00:18:00.000-07:00</published><updated>2009-03-01T20:43:16.363-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web service'/><title type='text'>SOAP中 RPC/encoded, RPC/literal, document/literal 之间区别</title><content type='html'>最近看了一个IBM的文章讲SOAP四种消息方式的差别&lt;br /&gt;&lt;br /&gt;RPC是面向调用的，所以要求在payload中包含operation名字&lt;br /&gt;而document方式是不包含operation名字的，payload里直接就是part&lt;br /&gt;&lt;br /&gt;encoded包含part的类型信息，比如xsi:type="xsd:int"&lt;br /&gt;literal是不包含part的类型信息但是通过引用schema里的元素也可以容易的通过schema验证&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;RPC/encoded&lt;/span&gt;&lt;br /&gt;WSDL:&lt;br /&gt;&amp;lt;message name="myMethodRequest"&amp;gt;&lt;br /&gt;    &amp;lt;part name="x" type="xsd:int"/&amp;gt;&lt;br /&gt;    &amp;lt;part name="y" type="xsd:float"/&amp;gt;&lt;br /&gt;&amp;lt;/message&amp;gt;&lt;br /&gt;&amp;lt;message name="empty"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;portType name="PT"&amp;gt;&lt;br /&gt;    &amp;lt;operation name="myMethod"&amp;gt;&lt;br /&gt;        &amp;lt;input message="myMethodRequest"/&amp;gt;&lt;br /&gt;        &amp;lt;output message="empty"/&amp;gt;&lt;br /&gt;    &amp;lt;/operation&amp;gt;&lt;br /&gt;&amp;lt;/portType&amp;gt;&lt;br /&gt;&lt;br /&gt;Payload on wire:&lt;br /&gt; &amp;lt;myMethod&amp;gt;&lt;br /&gt;            &amp;lt;x xsi:type="xsd:int"&amp;gt;5&amp;lt;/x&amp;gt;&lt;br /&gt;            &amp;lt;y xsi:type="xsd:float"&amp;gt;5.0&amp;lt;/y&amp;gt;&lt;br /&gt; &amp;lt;/myMethod&amp;gt;&lt;br /&gt;优点：&lt;br /&gt; 服务端通过payload的顶层元素operation-name就可以分发请求到底层实现类，不需要根据myMethod内的几个part的类型来找到到底调用的哪一个operation&lt;br /&gt;缺点：&lt;br /&gt; xsi:type="xsd:int"这种太长，也没必要，性能下降&lt;br /&gt; 验证麻烦，x, y是单独通过schema type验证，而myMethod不属于schema，属于WSDL定义。&lt;br /&gt; 不兼容WS-I，所以互操作性有问题 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;RPC/Literal&lt;/span&gt;&lt;br /&gt;WSDL: 和RPC/encoded一样 &lt;br /&gt;&lt;br /&gt;Payload on wire:&lt;br /&gt;        &amp;lt;myMethod&amp;gt;&lt;br /&gt;            &amp;lt;x&amp;gt;5&amp;lt;/x&amp;gt;&lt;br /&gt;            &amp;lt;y&amp;gt;5.0&amp;lt;/y&amp;gt;&lt;br /&gt;        &amp;lt;/myMethod&amp;gt;&lt;br /&gt;优点：&lt;br /&gt; 服务端通过payload的顶层元素operation-name就可以分发请求到底层实现类，和RPC/encoded一样&lt;br /&gt; type encoding 没有了，性能能上升&lt;br /&gt; RPC/literal is WS-I compliant.&lt;br /&gt;缺点：&lt;br /&gt; 不容易验证message因为不包含x和y的类型信息，myMethod也不是schema定义的。&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Document/encoded&lt;/span&gt;&lt;br /&gt;Nobody follows this style. It is not WS-I compliant. So let's move on.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Document/Literal&lt;/span&gt;&lt;br /&gt;WSDL:多了包含schema的types元素&lt;br /&gt;&amp;lt;types&amp;gt;&lt;br /&gt;    &amp;lt;schema&amp;gt;&lt;br /&gt;        &amp;lt;element name="xElement" type="xsd:int"/&amp;gt;&lt;br /&gt;        &amp;lt;element name="yElement" type="xsd:float"/&amp;gt;&lt;br /&gt;    &amp;lt;/schema&amp;gt;&lt;br /&gt;&amp;lt;/types&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;message name="myMethodRequest"&amp;gt;&lt;br /&gt;    &amp;lt;part name="x" element="xElement"/&amp;gt;&lt;br /&gt;    &amp;lt;part name="y" element="yElement"/&amp;gt;&lt;br /&gt;&amp;lt;/message&amp;gt;&lt;br /&gt;&amp;lt;message name="empty"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;portType name="PT"&amp;gt;&lt;br /&gt;    &amp;lt;operation name="myMethod"&amp;gt;&lt;br /&gt;        &amp;lt;input message="myMethodRequest"/&amp;gt;&lt;br /&gt;        &amp;lt;output message="empty"/&amp;gt;&lt;br /&gt;    &amp;lt;/operation&amp;gt;&lt;br /&gt;&amp;lt;/portType&amp;gt;&lt;br /&gt;&lt;br /&gt;Payload on wire:&lt;br /&gt; &amp;lt;xElement&amp;gt;5&amp;lt;/xElement&amp;gt;&lt;br /&gt;        &amp;lt;yElement&amp;gt;5.0&amp;lt;/yElement&amp;gt;&lt;br /&gt;&lt;br /&gt;优点：&lt;br /&gt; 没有type信息，消息简短，传输性能好&lt;br /&gt; 所有payload内的内容都可以通过schema校验(xElement是引用types里的schema)&lt;br /&gt; WS-I部分兼容(只有一个元素的时候才兼容)&lt;br /&gt;缺点：&lt;br /&gt; WSDL复杂，包含了太多types&lt;br /&gt; operation name没有了，分发比较复杂(后面解释soapAction)&lt;br /&gt; WS-I要求soap:body内只有一个元素，而这里有了两个，所以有时候不兼容WS-I&lt;br /&gt;&lt;br /&gt;其中在HTTP头里加入soapAction可以解决分发问题，SOAP 1.1规范里说：The SOAPAction HTTP request header field can be used to indicate the intent of the SOAP HTTP request.  The value is a URI identifying the intent&lt;br /&gt;那么intent是什么呢？你需要在WSDL定义operation的时候给operation指定soapAction，这相当于一个operation的key或者id&lt;br /&gt;当document/literal的时候，如果有两个operation用了同样的参数(类似于方法重载), 那么服务端无法区分是调用了哪个operation&lt;br /&gt;这种情况下，必须要包含soapActiont头来指明到底哪个operation被调用。当然soapAction不要太长，否则还不如包含type的效率高&lt;br /&gt;注意：在SOAP 1.2协议中，soapActionb变成了action。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Document/literal wrapped pattern&lt;/span&gt;&lt;br /&gt;从这看出来，就算是document/literal虽然可以校验message但是没有了operation,RPC/literal相反，有operation但是校验message比较麻烦&lt;br /&gt;如果两点都能满足的话多好？恩，现在事实上从微软开始，很多人逐渐采用了document/literal wrapped pattern来解决这个问题.&lt;br /&gt;&lt;br /&gt;WSDL:&lt;br /&gt;&amp;lt;types&amp;gt;&lt;br /&gt;    &amp;lt;schema&amp;gt;&lt;br /&gt;        &amp;lt;element name="myMethodRequest"&amp;gt;&lt;br /&gt;            &amp;lt;complexType&amp;gt;&lt;br /&gt;                &amp;lt;sequence&amp;gt;&lt;br /&gt;                    &amp;lt;element name="x" type="xsd:int"/&amp;gt;&lt;br /&gt;                    &amp;lt;element name="y" type="xsd:float"/&amp;gt;&lt;br /&gt;                &amp;lt;/sequence&amp;gt;&lt;br /&gt;            &amp;lt;/complexType&amp;gt;&lt;br /&gt;        &amp;lt;/element&amp;gt;&lt;br /&gt;        &amp;lt;element name="myMethodResponse"&amp;gt;&lt;br /&gt;            &amp;lt;complexType/&amp;gt;&lt;br /&gt;        &amp;lt;/element&amp;gt;&lt;br /&gt;    &amp;lt;/schema&amp;gt;&lt;br /&gt;&amp;lt;/types&amp;gt;&lt;br /&gt;&amp;lt;message name="myMethodRequest"&amp;gt;&lt;br /&gt;    &amp;lt;part name="parameters" element="myMethodRequest"/&amp;gt;&lt;br /&gt;&amp;lt;/message&amp;gt;&lt;br /&gt;&amp;lt;message name="myMethodResponse"&amp;gt;&lt;br /&gt;    &amp;lt;part name="parameters" element="myMethodResponse"/&amp;gt;&lt;br /&gt;&amp;lt;/message&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;portType name="PT"&amp;gt;&lt;br /&gt;    &amp;lt;operation name="myMethod"&amp;gt;&lt;br /&gt;        &amp;lt;input message="myMethodRequest"/&amp;gt;&lt;br /&gt;        &amp;lt;output message="myMethodResponse"/&amp;gt;&lt;br /&gt;    &amp;lt;/operation&amp;gt;&lt;br /&gt;&amp;lt;/portType&amp;gt;&lt;br /&gt;&lt;br /&gt;Payload on wire&lt;br /&gt;        &amp;lt;myMethod&amp;gt;&lt;br /&gt;            &amp;lt;x&amp;gt;5&amp;lt;/x&amp;gt;&lt;br /&gt;            &amp;lt;y&amp;gt;5.0&amp;lt;/y&amp;gt;&lt;br /&gt;        &amp;lt;/myMethod&amp;gt;&lt;br /&gt;这么乍一看，payload和RPC/literal一样的，没有什么变化，其实不然。在RPC/literal中myMethod就是operation。但是在docment/literal wrapped中，myMethod是wrapper的名字，这个可以通过payload中唯一的input message得到operation，这样operation也可以得到了。&lt;br /&gt;&lt;br /&gt;对于document/literal wrapped方式，有几个特点：&lt;br /&gt; 对于每一个operation, input message只有一个&lt;br /&gt; part不是primitive类型，是一个complex element&lt;br /&gt; wrapper和operation名字一样&lt;br /&gt; wrapper没有任何atrribute&lt;br /&gt;&lt;br /&gt;优点：&lt;br /&gt; 没有type encoding，消息短小，效率高&lt;br /&gt; payload所有元素都可以有对应的schema校验(从根元素myMethod开始)&lt;br /&gt; soap body里的wrapper就是operation，所以容易分发&lt;br /&gt; wrapper增加的约束使得document/literal方式的payload只有一个元素(myMethod)，满足了WS-I&lt;br /&gt;缺点：&lt;br /&gt; WSDL太长&lt;br /&gt; 本身只是一个style，不是标准或者规范&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-3795267453045242687?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/3795267453045242687/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=3795267453045242687' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/3795267453045242687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/3795267453045242687'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/10/soap-rpcencoded-rpcliteral.html' title='SOAP中 RPC/encoded, RPC/literal, document/literal 之间区别'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-2758540601226503739</id><published>2008-10-10T19:49:00.000-07:00</published><updated>2008-11-02T23:47:12.234-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web service'/><title type='text'>一点点关于Restful Web Service的设计的思考</title><content type='html'>Restful Web Service 的五条原则：&lt;br /&gt;    * 为所有“事物”定义ID&lt;br /&gt;    * 将所有事物链接在一起&lt;br /&gt;    * 使用标准方法&lt;br /&gt;    * 资源多重表述&lt;br /&gt;    * 无状态通信&lt;br /&gt;&lt;br /&gt;如果你要实现这样的Restful WS那么你可能会遇到和我遇到的一样的一些问题：&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;问题1：面向资源和面向消息动作的服务&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Restful Web Service是面向资源的服务，不同于SOAP是面向消息和动作的服务，Rest WS应该用URI来表示资源.&lt;br /&gt;这就有一个问题，对于资源来讲是没有业务语义的，比如一个缺陷跟踪软件如果需要暴露这样两个服务：&lt;br /&gt;1） update一个bug，对于SOAP来讲，在WSDL里暴露一个update的operation，服务端代码更新数据库就好了&lt;br /&gt;2） reopen一个bug,对于SOAP来讲，你需要WSDL暴露一个operation是reopen，然后服务端代码除了会操作数据库更改bug的状态之外，还会发送email通知owner,并启动一个处理bug的工作流。&lt;br /&gt;这两个操作本质上都会update bug的字段，但是由于两个操作有业务语义，所以他们是不同的，后者会启动一个业务工作流，而前者不会。&lt;br /&gt;&lt;br /&gt;对于Restful Web Service来讲，更改bug在数据库的状态很简单，但是如何区分语义呢？我们知道这个HTTP PUT(或POST)操作在Restful WS中本身只知道我要更新资源，它本身是没有业务语义的，因为Rest WS是基于资源的服务，没有任何业务逻辑。&lt;br /&gt;这个问题确实比较头疼，在现实世界中，大多数复杂的应用是粗颗粒基于消息和动作的，比如RPC.如果你需要对资源操作，那么客户端就要负责业务逻辑和事务性等，这对客户端是一个很大的麻烦。比如一个SOAP里deletePersons( names[])对应到RestWS就要客户端循环发送DELETE /persons/name这个请求，对于更复杂的运算客户端不得不更多的了解服务端的内部数据关系，严重破坏了封装性。另外，这种情况下客户端也很难实现事务性。&lt;br /&gt;但是反过来说，现实世界中暴露的服务，可能80%的操作还是最基本的CRUD(增读改删)，这个比较适合Rest，清晰简单，可能20%的操作还是有很强的业务语义的操作，更适合SOAP，这个比例每个项目会不太一样，但是肯定是CRUD比较多。那么如果你要使用Restful WS如何平衡呢？我的想法是，做一些违反Restful规则的服务。比如deletePersons(names[])这个操作，你可以发一个请求 POST /persons?deletePersons，然后请求体包含一段XML或者JSON包含所有的name.事实上很多互联网网站提供的Restful API也是这种面向资源和动作的服务的混合体，正是那句话，没有银弹。&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;问题2，客户端说，该死，Restful不是规范所以更不会有WSDL，我怎么知道怎么调用？&lt;/span&gt;&lt;br /&gt;另外Restful WS没有WSDL那样的规范，即便你用JSON或者ATOM协议，具体内容(比如atom的content tag)仍然是没有具体规定的，你可以纯文本或者POX(Plain Old XML)或者base64的内容放到atom的content tag里，没有限制，如果资源是一个树状结构的复杂数据实体，那么客户端怎么才能知道如何产生请求报文和解析响应呢？&lt;br /&gt;我的想法：提供MDS(meta data service)，暴露一个全局的服务，告诉客户端每个服务支持哪些格式，比如如果你的内容是POX那么这个MDS应该会是很多schema，如果内容也可以是JSON格式，因为JSON没有schema，但是可以用BNF范式，不过BNF读起来实在不是很方便，你可以用JSON-XML转换的规则来套用XML的Schema （http://www.ibm.com/developerworks/library/x-atom2json.html， 我更喜欢这个 http://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html） 。还有一种方式，就是把复杂的资源拆分为多个小的资源，保证所有资源背后的数据都是flat的key/value pair，这样你就可以通过POST/PUT做表单提交一样的更新数据。但是这个工作有些时候对调用者不太方便，事务性也很难保证。&lt;br /&gt;&lt;br /&gt;Restful WS没有任何WS-Enumeration, WS-Policy, WS-Security, WS-Transaction这样的协议集合，所以你需要自己来实现，这是没有标准的。这么看来Restful WS确实比较单薄，但是或许正是因为Restful 比较简单所以才会使用越来越广泛。这也不是一个大问题，你可以尽量利用现有技术或者实现自己的方案，比如安全性你可以使用证书和HTTPS的保证验证和传输安全。&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;问题3，我该用PUT 还是 POST?&lt;/span&gt;&lt;br /&gt;根据HTTP规范，PUT适用于做安全的幂等性操作(idempotent),换句话说，两次操作是安全的，不会导致不同的结果。&lt;br /&gt;比如一条SQL:update gender='male' where name='daniel'执行两次不会造成不同的结果&lt;br /&gt;类似的还有:createOrUpdate(user1)调用两次也没有问题&lt;br /&gt;非幂等性的操作就不能保证这一点，比如，一条SQL:update counter=counter+1 where name='x'执行两次就会产生不同的结果，再比如create(user1)调用两次会创建两个一样的用户，这也会产生不同的结果。&lt;br /&gt;&lt;br /&gt;这意味着PUT其实可以看作是createOrUpdate操作，比如&lt;br /&gt;PUT /persons/daniel第一次会创建daniel这个人，第二次请求会更新daniel这个人，如果两个请求内容一样，那么更新操作其实不会对第一次创建的daniel做任何改动。&lt;br /&gt;&lt;br /&gt;和PUT一样GET,HEAD,PUT,DELETE,OPTIONS和TRACE都有这种性质.&lt;br /&gt;DELETE可以用于删除，GET可以用于读取。&lt;br /&gt;&lt;br /&gt;比较特殊的是POST，他蕴含的意思是，嘿，客户端，调用我的话，我可不保证幂等性，我可能会启动一个工作流，也可能会插入一个消息到另一个系统，我什么都能做，但我就是不能保证你调用我两次同样的请求会出现什么后果。&lt;br /&gt;上面我们提到过的Restful WS是没有面向消息和动作的服务的，如果我们要提供的话，其实POST是最好的选择，他可以做任何事情。而PUT更适合做面向资源的服务，用来创建或者更新一个数据。&lt;br /&gt;&lt;br /&gt;参考HTTP规范http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html：&lt;br /&gt;The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource. &lt;br /&gt;&lt;br /&gt;但是Atom Publishing Protocol这种经常被用作Restful WS的协议去不是这样的，她就是用PUT做更新，POST做创建。为什么呢？我觉得现实世界中，大多数情况下当你创建一个entry你是不知道他的主键的，这个主键90%的情况下都是服务器端生成的自增主键。如果你用PUT去创建一个entry，比如PUT /rest/person/daniel, 这隐含着daniel就是person的主键值，这里不能有两个人都叫daniel。而事实上当你创建一个新的person你通常是不指定主键值，而是服务器端返回一个社会福利号码或者身份证编号这样的主键，所以如果你还用PUT那么请求看起来应该是这样PUT /rest/person， 然后创建出来的person的URI /rest/person/12434320456。乍一看没问题，其实问题很严重，PUT URI之后，资源的URI不应该变化，这里你的URI从没有后面的数字ID变成了有数字ID, 这种对一个URL提交然后在服务端处理后在另外一个URL暴露你提交的数据，这不是POST在HTTP规范里定义的行为么？所以，呵呵，APP认为大多数情况下ID是服务端生成的，所以用POST更合适。&lt;br /&gt;&lt;br /&gt;那到底我们应该用什么呢？PUT还是POST创建记录?&lt;br /&gt;我觉得，如果你知道新纪录的主键，比如客户端可以生成GUID你就可以用PUT，90%的情况下都是自增主键，那你还是用POST。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-2758540601226503739?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/2758540601226503739/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=2758540601226503739' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/2758540601226503739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/2758540601226503739'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/10/restful-web-service.html' title='一点点关于Restful Web Service的设计的思考'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-3319023461136427957</id><published>2008-10-05T05:54:00.000-07:00</published><updated>2008-11-02T23:47:38.772-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Concurrency Cheat Sheet</title><content type='html'>&lt;span style="font-weight:bold;"&gt;It's the mutable state, stupid. &lt;/span&gt;&lt;br /&gt;All concurrency issues boil down to coordinating access to mutable state. The less mutable state, the easier it is to ensure thread safety.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Make fields final unless they need to be mutable.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Immutable objects are automatically thread-safe.&lt;/span&gt;&lt;br /&gt;Immutable objects simplify concurrent programming tremendously. They are simpler and safer, and can be shared freely without locking or defensive copying. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Encapsulation makes it practical to manage the complexity.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Guard each mutable variable with a lock.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Guard all variables in an invariant with the same lock.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Hold locks for the duration of compound actions.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;A program that accesses a mutable variable from multiple threads without synchronization is a broken program.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Don't rely on clever reasoning about why you don't need to synchronize.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Include thread safety in the design processor explicitly document that your class is not thread-safe.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Document your synchronization policy.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-3319023461136427957?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/3319023461136427957/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=3319023461136427957' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/3319023461136427957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/3319023461136427957'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/10/concurrency-cheat-sheet.html' title='Concurrency Cheat Sheet'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-8420425140937391211</id><published>2008-10-04T11:42:00.000-07:00</published><updated>2008-11-02T23:47:57.617-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>一个随便写写的测试Java reordering的程序</title><content type='html'>程序很简单，就是有一个类Obj不停的被实例化&lt;br /&gt;class Obj {&lt;br /&gt; int x;&lt;br /&gt; public Obj(int x) {&lt;br /&gt;  this.x = x;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;这个类总是被一个线程传入参数100不停的构造，另外一个线程读取，理论上另一个线程可能只会看到x=100对么？其实不是，如果java -server来执行这个程序你会看到很多x=0的情况出现。为什么呢？就是因为reordering的优化&lt;br /&gt;如果你给x加上volatile或者final关键字你就不会看到这个问题了，或者给obj加上volatile也可以。&lt;br /&gt;因为这两个关键字都禁止reordering，在JDK1.4里或许不行，但是JDK5重新规定了volatile的行为，其中包括了不仅和volatile变量之间禁止reordering，也和non-volatile变量之间禁止reordering了。&lt;br /&gt;final这里可能更好一些，因为Obj这样就是一个immutable对象了，而我们Obj的构造函数并没有escape this到外部线程，所以可以获得JDK5的所谓Initialization Safety的特性，保证了final字段x一定是不需要同步就可以被外部线程看到，这蕴含着禁止reordering.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;package test.jmm;&lt;br /&gt;&lt;br /&gt;public class TestReordering {&lt;br /&gt; public Obj obj=null;&lt;br /&gt; &lt;br /&gt; public static void main(String[] args) {&lt;br /&gt;  TestReordering test = new TestReordering();&lt;br /&gt;  new WriterThread(test).start();&lt;br /&gt;  new ReaderThread(test).start();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class Obj {&lt;br /&gt; int x;&lt;br /&gt; public Obj(int x) {&lt;br /&gt;  this.x = x;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class WriterThread extends Thread {&lt;br /&gt; private TestReordering test;&lt;br /&gt; public WriterThread(TestReordering test) {&lt;br /&gt;  this.test = test;&lt;br /&gt; }&lt;br /&gt; public void run() {&lt;br /&gt;  while(true) {&lt;br /&gt;   test.obj = new Obj(100);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;class ReaderThread extends Thread {&lt;br /&gt; private TestReordering test;&lt;br /&gt; public ReaderThread(TestReordering test) {&lt;br /&gt;  this.test = test;&lt;br /&gt; }&lt;br /&gt; public void run() {&lt;br /&gt;  while(true) {&lt;br /&gt;   if (test.obj != null)&lt;br /&gt;    if(test.obj.x == 0) {&lt;br /&gt;     System.out.println("found");&lt;br /&gt;    } else {&lt;br /&gt;     //System.out.println(test.obj.x);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-8420425140937391211?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/8420425140937391211/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=8420425140937391211' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/8420425140937391211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/8420425140937391211'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/10/reordering.html' title='一个随便写写的测试Java reordering的程序'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-5312164175172981610</id><published>2008-10-02T23:48:00.000-07:00</published><updated>2008-11-02T23:48:31.116-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Strictfp到底有多实用？</title><content type='html'>如果是为了满足IEEE-754 floating-point specification,那么是否意味着数学浮点协处理器单元就不能发挥最大的作用了？&lt;br /&gt;&lt;br /&gt;如果满足这个IEEE754是否表示我的程序可以移植？我觉得如果你的程序移植性依赖于浮点运算的结果本身就是个错误，所以这个问题似乎没有太大意义。&lt;br /&gt;&lt;br /&gt;如果我真的需要非常严格的数值，我可能会用BigDecimal&lt;br /&gt;&lt;br /&gt;所以，有多大用处呢？貌似很多5年Java经验的人也不太清楚strictfp。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-5312164175172981610?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/5312164175172981610/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=5312164175172981610' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5312164175172981610'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5312164175172981610'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/10/strictfp.html' title='Strictfp到底有多实用？'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-7789978945960772561</id><published>2008-10-02T20:49:00.000-07:00</published><updated>2008-11-02T23:56:25.621-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>浅谈JMM的reordering导致的一些问题</title><content type='html'>1997年开始JVM的内存问题开始逐渐暴露出来，不同于脚本语言的简单内存模型或者C的不完整内存模型，由于Java是历史上第一个提供完整的复杂的内存模型的语言和运行环境，有没有考虑周全的问题是必然的。这后来导致了一代宗师Doug Lea的JSR133。这引发了很多VM和语言本身的变化，比如volatile关键字的含义的变化。我觉得reordering是其中比较tricky的一个问题，直到JDK5才得以解决。&lt;br /&gt;&lt;br /&gt;什么是reordering问题？&lt;br /&gt;表示对代码指令的执行顺序的改变。比如先写a再写b,如果b和a不是互相依赖,编译器可以改成先写a和另外一个变量，凑成一个字长写回，这种优化不仅可能发生在编译期间，甚至发生在运行期。这些看起来是很聪明的优化，但是这些优化大多数只是对单线程的执行优化，在多线程的时候会导致问题。过去C是依赖于线程类库来保证内存操作的正确性，Java是通过JMM实现的。&lt;br /&gt;&lt;br /&gt;recording可以发生在几种情况如下：&lt;br /&gt;&lt;br /&gt;  *编译期间，编译器可以对某些指令重新排序甚至改变来提高性能，比如inline&lt;br /&gt;  *运行期间，处理器在某些情况下可能会改变指令的运算顺序来提高性能，比如64位子长机器为了一次写两个32位的值&lt;br /&gt;  *运行期间，缓存(寄存器)有可能不是按照指令的顺序写回更新&lt;br /&gt;&lt;br /&gt;比如Doug Lea的一个例子：&lt;br /&gt;Class Reordering {&lt;br /&gt;  int x = 0, y = 0;&lt;br /&gt;  public void writer() {&lt;br /&gt;    x = 1;&lt;br /&gt;    y = 2;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void reader() {&lt;br /&gt;    int r1 = y;&lt;br /&gt;    int r2 = x;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;一个线程调用writer的时候如果处理器发生了reordering，可能会先执行y=2然后再执行x=1。如果这时候另外一个线程调用reader，y被读取的时候可以看到2但是不一定能看到x=1,很可能看到x=0，因为writer执行的顺序发生了变化.&lt;br /&gt;&lt;br /&gt;另外，对于比较短小的函数，编译器会做inline，和C++里inline关键字含义很类似，这样可以减少方法调用栈的频繁压栈弹栈来提升性能。比如这样一个构造函数&lt;br /&gt;public Foo {&lt;br /&gt;  public int x = 0;&lt;br /&gt;  public Foo(int v) {&lt;br /&gt;    x=v;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;当编译Foo f = new Foo()的时候，编译器会inline成两行普通代码：&lt;br /&gt;Foo f = 分配内存; &lt;br /&gt;f.x=0;&lt;br /&gt;f.x=v;&lt;br /&gt;这样导致一个问题就是如果另外一个线程看到x的时候可能是0,稍后又变成v.这会导致另外一个线程看到了一个不完全构造的Foo对象！即便你在构造函数内没有写类似于global.ref=this，即便你没有escape this reference到外部线程，由于这种inline+reordering的优化，外部线程仍然可以看到半成品的对象。这对于一般的情况我们可能会考虑把x作为私有的不让外部线程访问，或者其他线程访问的时候同步,因为同步会除了mutex和flush还会设立reordering的上下边界，也就是说在同步边界上，另外一个线程不会先获得s2的引用，必须退出同步块，s2完全构造结束后另一个线程才能得到s2的引用。但是对于我们通常理解final应该不受这个约束的，事实上老的JMM不是这样处理的，这对于final的字段是一场灾难。&lt;br /&gt;&lt;br /&gt;final问题：&lt;br /&gt;过去final没有被特殊处理，这导致final的东西不final，会发生很困惑不可预料的问题。&lt;br /&gt;比如String这个类，包含了三个final成员变量，分别是长度，偏移量和一个字符数组。&lt;br /&gt;下面的两行代码如果被一个线程执行到第二行的时候，&lt;br /&gt;String s1 = "/usr/tmp";&lt;br /&gt;String s2 = s1.substring(4); &lt;br /&gt;JVM为s1.substring()所将要产生的String调用parent constructor Object()，这时候heap分配Object所需要的内存并把地址赋值给s2，然后所有final的成员变量会被初始化为0,然后String的构造函数再把offset和length设置为期望的值。由于在老的JMM里在内存地址先复制给s2然后再调用String的构造函数就会导致另外一个线程如果这时候使用了s2就会看到offset=0，也就是说s2="/usr/tmp"，稍后再一次使用s2又会看到s2="/tmp"&lt;br /&gt;这样感觉就是final的字段也会变化，immutable的String也会变化，这个问题会非常难以发现。JSR133新的内存模型改变了这种行为，叫做initialization safety. 意思是说当一个对象构造过程中没有把自己通过引用暴露给其他线程，构造函数结束后，这个对象才算安全构造成功，之后这个对象的内部所有的final字段都可以不需要同步就可以让外部的线程看到。这也隐含着新的JMM对s2的赋值会发生在构造函数完全结束，如果有final字段的话。&lt;br /&gt;&lt;br /&gt;volatile问题&lt;br /&gt;一般来说普通变量会被保存到线程级别local copy，通常是寄存器，用的时候直接从速度最快的寄存器读取，避免多次频繁的寻址读取主存，这就意味着在某些情况下必须在主存和local copy之间flush.这种flush保证了同步，保证了happens before 原则。&lt;br /&gt;而volatile他会总是访问主存而不是local copy，换句话说运算的时候，每次都是重新读取内存然后写入寄存器再参与计算,L1/L2 cache也不会缓存。 &lt;br /&gt;这样他总是可以看到其他线程对主存的最新的操作结果。但是这不能保证独享，因为没有lock&lt;br /&gt;比如两个线程都进行a++，那么理路上可能两个线程都看到了初始值0，然后都累加到1，都在某个flush的时候写回主存，结果是最终结果不是2，而是1。所以这个不可以用于计数器或者信号量。&lt;br /&gt;&lt;br /&gt;但是他更快更简单，不需要锁，但是如果你有大量的volatile变量参与运算，那么大量的main memory操作也可能会比较慢，这时候你也可以考虑用lock批量的操作之后flush回main memory。&lt;br /&gt;&lt;br /&gt;老的JMM中volatile是仅仅不可以和volatile变量之间reordering,但和普通变量之间是可以发生reordering的，这导致实用性大大下降。Doug Lea给了一个例子：&lt;br /&gt;class VolatileExample {&lt;br /&gt;  int x = 0;&lt;br /&gt;  volatile boolean v = false;&lt;br /&gt;  public void writer() {&lt;br /&gt;    x = 42;&lt;br /&gt;    v = true;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void reader() {&lt;br /&gt;    if (v == true) {&lt;br /&gt;      //uses x - guaranteed to see 42.&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;如果在老的JMM中，x和v可以reorder，处理器可能先写了v=true，这个时候还没有写x=42,恰巧这个时候另外一个线程执行reader()，那么这个线程可以看到v=true，然后认为x一定已经是42了，其实不然，这时候x是0！只有在新的JMM中，volatile和non-volatile彼此不允许在一个方法调用栈中recorder的时候才能保证数据操作的顺序性。注意：volatile还保证原子性。对于long和double由于是64位，其他primitives类型是32位，所以在32位字长的系统上long和double不能保证原子性，这时候需要volatile.对于64为系统是否需要volatile我不敢说，但是我会安全起见也加上volatile，毕竟Java会运行在不同的硬件平台上。&lt;br /&gt;&lt;br /&gt;一个相关的有趣的问题可以在这里找到，volatile禁止reordering最终解决了DCL问题。&lt;br /&gt;http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html&lt;br /&gt;&lt;br /&gt;这里我只是简单介绍一下：&lt;br /&gt;下面这段代码想避免每次都要同步的开销同时保证只创建一次helper.&lt;br /&gt;&lt;br /&gt;class Foo { &lt;br /&gt;  private Helper helper = null;&lt;br /&gt;  public Helper getHelper() {&lt;br /&gt;    if (helper == null) &lt;br /&gt;      synchronized(this) {&lt;br /&gt;        if (helper == null) &lt;br /&gt;          helper = new Helper();&lt;br /&gt;      }    &lt;br /&gt;    return helper;&lt;br /&gt;    }&lt;br /&gt;  // other functions and members...&lt;br /&gt;  }&lt;br /&gt;其实这里有个问题，helper的构造可能会被Inline+reorder,导致赋值和构造顺序的变化，所以你是没有办法保证得到一个完全构造好的helper的，除非helper所有的字段都是final的,而且运行在JDK5下。有了volatile可以禁止这种优化，下面的代码就可以正常运行：&lt;br /&gt;&lt;br /&gt;  class Foo {&lt;br /&gt;        private volatile Helper helper = null;&lt;br /&gt;        public Helper getHelper() {&lt;br /&gt;            if (helper == null) {&lt;br /&gt;                synchronized(this) {&lt;br /&gt;                    if (helper == null)&lt;br /&gt;                        helper = new Helper();&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            return helper;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;当然这里注意的是对于32位的primitive，不加volatile的DCL也是可以的，因为32位字长的机器写32位的int/float本身就是硬件提供了原子操作的能力的，但是32位子长的机器写64位的long和double的时候，也一定要加volatile，因为第一个进入的线程写高32位和低32位之间，如果另外一个线程进入同步快会看到helper不等于0(其实是写了一半的数值)，第二个线程就会面临一场灾难了.&lt;br /&gt;&lt;br /&gt;volatile还有一些技巧，比如当作cheap lock用：&lt;br /&gt;public class CheesyCounter {&lt;br /&gt;    private volatile int value;&lt;br /&gt;    public int getValue() { return value; }&lt;br /&gt;    public synchronized int increment() {&lt;br /&gt;        return value++;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;小结，JMM其实更为复杂的多，写出伸缩性好的程序一定要对JMM有基本的理解，因为JMM其实主要是和线程相关的，而大规模的应用几乎没有不使用线程的，而且可能是非常intensive的使用。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-7789978945960772561?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/7789978945960772561/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=7789978945960772561' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/7789978945960772561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/7789978945960772561'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/10/jmmreordering.html' title='浅谈JMM的reordering导致的一些问题'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-7328041449239366537</id><published>2008-09-25T07:45:00.000-07:00</published><updated>2008-11-02T23:49:54.166-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>escape analysis 和  lock elision</title><content type='html'>Escape analysis 是检查所有引用的作用域, 如果一个引用只是在一个本地作用域内，没有escape到更大的作用域， JIT编译成本地代码的时候会进行实时优化。&lt;br /&gt;比如最重要的一个就是锁擦除 lock elision.&lt;br /&gt;如果escape analysis发觉这个引用只在一个本地作用域(局部变量)，这表示只有在当前作用域的线程才有可能访问这个锁，在这种情况下永远不可能有别的线程进入这个作用域和当前线程竞争，所以这种情况下这个锁可以被消除，这就是lock elision.&lt;br /&gt;&lt;br /&gt;比如这段代码：&lt;br /&gt;public String concatBuffer(String s1, String s2) {&lt;br /&gt; StringBuffer sb = new StringBuffer();&lt;br /&gt; sb.append(s1);&lt;br /&gt; sb.append(s2);&lt;br /&gt; return sb.toString();&lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;这种情况下StringBuffer对象的作用域就这这个方法内，所以StringBuffer完全没有必要线程安全加锁（当然更好的做法是用StringBuilder，不要依赖于聪明但是有时候行为不可预料的编译器和JIT/JRE.）, 所以escape analysis结果表示可以优化，lock elision这种情况下就可以发生。&lt;br /&gt;&lt;br /&gt;以下是测试程序&lt;br /&gt;package test.thread;&lt;br /&gt;&lt;br /&gt;public class LockElisionTest {&lt;br /&gt;&lt;br /&gt; public static void main(String[] args) {&lt;br /&gt;  long t1 = System.currentTimeMillis();&lt;br /&gt;  LockElisionTest obj = new LockElisionTest();&lt;br /&gt;  for (int i = 0; i &lt; 10000000; i++) {&lt;br /&gt;   obj.concatBuffer("1", "2");&lt;br /&gt;  }&lt;br /&gt;  long t2 = System.currentTimeMillis();&lt;br /&gt;  System.out.println(t2 - t1);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String concatBuffer(String s1, String s2) {&lt;br /&gt;   StringBuffer sb = new StringBuffer();&lt;br /&gt;   sb.append(s1);&lt;br /&gt;   sb.append(s2);&lt;br /&gt;   return sb.toString();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;注意的是是-XX:+DoEscapeAnalysis默认没有打开，而且你需要用-server模式&lt;br /&gt;&lt;br /&gt;结果JRE5没有打开escape analysis是3500毫秒&lt;br /&gt;JRE6打开是2100毫秒，关闭2200毫秒左右(估计因为还有别的优化)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-7328041449239366537?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/7328041449239366537/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=7328041449239366537' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/7328041449239366537'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/7328041449239366537'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/09/escape-analysis-lock-elision.html' title='escape analysis 和  lock elision'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-8839955482343701776</id><published>2008-09-25T07:19:00.000-07:00</published><updated>2008-11-02T23:55:26.548-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Java Biased Lock 实验</title><content type='html'>今天神舟七号升天，不知道是不是去成都的飞机都受影响，19：50的飞机改到23：20，在机场无聊，写篇博客吧&lt;br /&gt;&lt;br /&gt;什么是biased lock?&lt;br /&gt;Biased Locking is a class of optimizations that improves uncontended synchronization performance by eliminating atomic operations associated with the Java language’s synchronization primitives. &lt;br /&gt;&lt;br /&gt;有些时候一个线程多次获得对象锁的操作中，理论上这些操作可以合并在一起，而减少lock/unlock的时间。不过这样会导致对其他线程的不公平，所以叫biased lock.通常是对第一个获得锁的线程偏心。&lt;br /&gt;&lt;br /&gt;禁止：-XX:-UseBiasedLocking&lt;br /&gt;使用：JRE6默认使用，或者显式使用-XX:+UseBiasedLocking&lt;br /&gt;以下是我的测试程序，在JRE6上使用biased lock只需要500毫秒左右，而使用JRE5则需要5500毫秒左右，快10倍以上。&lt;br /&gt;奇怪的是JRE6上禁止biased lock之后貌似性能没有什么变化,如果你打印一些调试信息（关闭注释System.out.println("T" + id + " holds lock")那行）你会发现仍然是biased lock，知道一个线程结束才释放锁。&lt;br /&gt;&lt;br /&gt;package test.thread;&lt;br /&gt;&lt;br /&gt;import java.util.concurrent.CountDownLatch;&lt;br /&gt;&lt;br /&gt;public class BiasedLockTest {&lt;br /&gt; &lt;br /&gt; public static volatile Long t1 = System.nanoTime();&lt;br /&gt; static {&lt;br /&gt;  System.out.println(t1);&lt;br /&gt; }&lt;br /&gt; public static volatile Long t2;&lt;br /&gt; &lt;br /&gt; public static void main(String[] args) throws InterruptedException {&lt;br /&gt;  &lt;br /&gt;  Object lock = new Object();&lt;br /&gt;  &lt;br /&gt;  TestThread[] threads = new TestThread[100];&lt;br /&gt;  CountDownLatch latch = new CountDownLatch(threads.length);&lt;br /&gt;  &lt;br /&gt;  for (int i = 0; i &lt; 100; i++) {&lt;br /&gt;   threads[i] = new TestThread(i, lock, latch);&lt;br /&gt;  }&lt;br /&gt;  for (int i = 0; i &lt; 100; i++) {&lt;br /&gt;   threads[i].start();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  for (int i = 0; i &lt; 100; i++) {&lt;br /&gt;   threads[i].join();&lt;br /&gt;  }&lt;br /&gt;  System.out.println(t2);&lt;br /&gt;  System.out.println( (t2 - t1) / 10e5);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class TestThread extends Thread {&lt;br /&gt; final private int id;&lt;br /&gt; final private Object lock;&lt;br /&gt; final private CountDownLatch latch;&lt;br /&gt; &lt;br /&gt; public TestThread(int id, Object lock, CountDownLatch latch) {&lt;br /&gt;  this.id = id;&lt;br /&gt;  this.lock = lock;&lt;br /&gt;  this.latch = latch;&lt;br /&gt; }&lt;br /&gt; public void run() {&lt;br /&gt;  for(int i = 0; i &lt; 10000; i++) {&lt;br /&gt;   synchronized (lock) {&lt;br /&gt;    int k = 0;&lt;br /&gt;    k=k+1;//dummy calculation&lt;br /&gt;   }&lt;br /&gt;//   System.out.println("T" + id + " holds lock");&lt;br /&gt;  }&lt;br /&gt;  BiasedLockTest.t2 = System.nanoTime();&lt;br /&gt;  latch.countDown();&lt;br /&gt;//  System.out.println("T" + id + " dies");&lt;br /&gt; }&lt;br /&gt;}&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-8839955482343701776?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/8839955482343701776/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=8839955482343701776' title='4 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/8839955482343701776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/8839955482343701776'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/09/java-biased-lock.html' title='Java Biased Lock 实验'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-5935385821380005808</id><published>2008-09-23T23:24:00.000-07:00</published><updated>2008-11-02T23:52:53.138-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>把金山词霸添加到google bar按钮查询</title><content type='html'>安装了google金山词霸合作版之后，今天查了几个词都没有查到，觉得很差劲。我甚至尝试性搜索了一个最简单的gonna都查不到，一气之下反安装了。突然想起iciba.com还是不错的，我又用google bar，能不能做一个按钮呢？&lt;br /&gt;首先随便添加任何一个google button，然后选项里点edit, advanced editor, 把内容换成这个&lt;br /&gt;&amp;lt;custombuttons xmlns="http://toolbar.google.com/custombuttons/"&amp;gt;&lt;br /&gt;  &amp;lt;button&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;iciba&amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;description&amp;gt;www.iciba.com/&amp;lt;/description&amp;gt;&lt;br /&gt;    &amp;lt;icon mode="base64" type="image/x-icon"&amp;gt;&lt;br /&gt;iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAMAAADH72RtAAAAAXNSR0IArs4c6QAAAARnQU1BAACx&lt;br /&gt;jwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAwBQTFRF&lt;br /&gt;AAAAgAAAAIAAgIAAAACAgACAAICAgICAwMDA/wAAAP8A//8AAAD//wD/AP//////AAAAAAAAAAAA&lt;br /&gt;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&lt;br /&gt;AAAAAAAAAAAAAAAzAABmAACZAADMAAD/ADMAADMzADNmADOZADPMADP/AGYAAGYzAGZmAGaZAGbM&lt;br /&gt;AGb/AJkAAJkzAJlmAJmZAJnMAJn/AMwAAMwzAMxmAMyZAMzMAMz/AP8AAP8zAP9mAP+ZAP/MAP//&lt;br /&gt;MwAAMwAzMwBmMwCZMwDMMwD/MzMAMzMzMzNmMzOZMzPMMzP/M2YAM2YzM2ZmM2aZM2bMM2b/M5kA&lt;br /&gt;M5kzM5lmM5mZM5nMM5n/M8wAM8wzM8xmM8yZM8zMM8z/M/8AM/8zM/9mM/+ZM//MM///ZgAAZgAz&lt;br /&gt;ZgBmZgCZZgDMZgD/ZjMAZjMzZjNmZjOZZjPMZjP/ZmYAZmYzZmZmZmaZZmbMZmb/ZpkAZpkzZplm&lt;br /&gt;ZpmZZpnMZpn/ZswAZswzZsxmZsyZZszMZsz/Zv8AZv8zZv9mZv+ZZv/MZv//mQAAmQAzmQBmmQCZ&lt;br /&gt;mQDMmQD/mTMAmTMzmTNmmTOZmTPMmTP/mWYAmWYzmWZmmWaZmWbMmWb/mZkAmZkzmZlmmZmZmZnM&lt;br /&gt;mZn/mcwAmcwzmcxmmcyZmczMmcz/mf8Amf8zmf9mmf+Zmf/Mmf//zAAAzAAzzABmzACZzADMzAD/&lt;br /&gt;zDMAzDMzzDNmzDOZzDPMzDP/zGYAzGYzzGZmzGaZzGbMzGb/zJkAzJkzzJlmzJmZzJnMzJn/zMwA&lt;br /&gt;zMwzzMxmzMyZzMzMzMz/zP8AzP8zzP9mzP+ZzP/MzP///wAA/wAz/wBm/wCZ/wDM/wD//zMA/zMz&lt;br /&gt;/zNm/zOZ/zPM/zP//2YA/2Yz/2Zm/2aZ/2bM/2b//5kA/5kz/5lm/5mZ/5nM/5n//8wA/8wz/8xm&lt;br /&gt;/8yZ/8zM/8z///8A//8z//9m//+Z///M////RGKwUAAAAKpJREFUKFNNzrEKAjEMBuCKBSlXzLsJ&lt;br /&gt;t/VRQo9b9B18g3Pr1Ew39RG6dXRXHG4Sirmz1f6hFD6SEBFjxPL44wiGDDmsWmWDpRHsIONtGE6/&lt;br /&gt;Htw/8OwdXGydwq7TKTmA8VD2sBgA71IrEiB5Z5oe5KY0jtN/KqCUUoNWnDkK3hPwStSbxDTnTZ6k&lt;br /&gt;uHpz1DN8JdDrzlkJqMi6QaneWLIs7yXsqBYfLibbhg/4ACM/k1kU4ql2AAAAAElFTkSuQmCC&lt;br /&gt;    &amp;lt;/icon&amp;gt;&lt;br /&gt;&amp;lt;search&amp;gt;http://www.iciba.com/{query}&amp;lt;/search&amp;gt;    &lt;br /&gt;   &amp;lt;/button&amp;gt;&lt;br /&gt;&amp;lt;/custombuttons&amp;gt;&lt;br /&gt;&lt;br /&gt;就行了，如果要换图标去这里生成base64&lt;br /&gt;http://www.motobit.com/util/base64-decoder-encoder.asp&lt;br /&gt;&lt;br /&gt;替换&amp;lt;icon&amp;gt;就可以了&lt;br /&gt;&lt;br /&gt;现在你在google bar搜索框写进去一个单词，比如gonna然后点击词霸的图标看看结果如何？&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-5935385821380005808?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/5935385821380005808/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=5935385821380005808' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5935385821380005808'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5935385821380005808'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/09/google-bar.html' title='把金山词霸添加到google bar按钮查询'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-4911693495403306291</id><published>2008-09-21T21:49:00.000-07:00</published><updated>2008-11-03T00:11:51.569-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>CentOS 安装 Subversion</title><content type='html'>1. 下载collabnet subversion server rpm包&lt;br /&gt;2. 安装rpm包rpm -iv client then server&lt;br /&gt;3. 创建启动脚本 as /etc/init.d/svnserve&lt;br /&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;#&lt;br /&gt;#   /etc/rc.d/init.d/svnserve&lt;br /&gt;#&lt;br /&gt;# Starts the Subversion Daemon&lt;br /&gt;#&lt;br /&gt;# chkconfig: 345 90 10&lt;br /&gt;# description: Subversion Daemon&lt;br /&gt;&lt;br /&gt;# processname: svnserve&lt;br /&gt;&lt;br /&gt;source /etc/rc.d/init.d/functions&lt;br /&gt;&lt;br /&gt;[ -x /usr/bin/svnserve ] || exit 1&lt;br /&gt;&lt;br /&gt;# To pass additional options (for instace, -r root of directory to server) to&lt;br /&gt;# the svnserve binary at startup, set OPTIONS here.&lt;br /&gt;#&lt;br /&gt;OPTIONS="-r /svn"&lt;br /&gt;RETVAL=0&lt;br /&gt;prog="svnserve"&lt;br /&gt;desc="Subversion Daemon"&lt;br /&gt;&lt;br /&gt;start() {&lt;br /&gt;        echo -n $"Starting $desc ($prog): "&lt;br /&gt;   daemon $prog -d $OPTIONS&lt;br /&gt;   RETVAL=$?&lt;br /&gt;   [ $RETVAL -eq 0 ] &amp;&amp; touch /var/lock/subsys/$prog&lt;br /&gt;   echo&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;stop() {&lt;br /&gt;   echo -n $"Shutting down $desc ($prog): "&lt;br /&gt;   killproc $prog&lt;br /&gt;   RETVAL=$?&lt;br /&gt;   [ $RETVAL -eq 0 ] &amp;&amp; success || failure&lt;br /&gt;   echo&lt;br /&gt;   [ $RETVAL -eq 0 ] &amp;&amp; rm -f /var/lock/subsys/$prog&lt;br /&gt;   return $RETVAL&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;case "$1" in&lt;br /&gt;  start)&lt;br /&gt;   start&lt;br /&gt;   ;;&lt;br /&gt;  stop)&lt;br /&gt;   stop&lt;br /&gt;   ;;&lt;br /&gt;  restart)&lt;br /&gt;   stop&lt;br /&gt;   start&lt;br /&gt;   RETVAL=$?&lt;br /&gt;   ;;&lt;br /&gt;  condrestart)&lt;br /&gt;        [ -e /var/lock/subsys/$prog ] &amp;&amp; restart&lt;br /&gt;   RETVAL=$?&lt;br /&gt;   ;;&lt;br /&gt;  *)&lt;br /&gt;   echo $"Usage: $0 {start|stop|restart|condrestart}"&lt;br /&gt;   RETVAL=1&lt;br /&gt;esac&lt;br /&gt;&lt;br /&gt;exit $RETVAL&lt;br /&gt;&lt;br /&gt;添加启动脚本到run level 3 和 5&lt;br /&gt;[root@alpha init.d]# chkconfig --add svnserve&lt;br /&gt;[root@alpha init.d]# chkconfig --level 35 svnserve on&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;现在启动客户端比如svn tortoise，browse repository就可以了&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-4911693495403306291?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/4911693495403306291/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=4911693495403306291' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4911693495403306291'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4911693495403306291'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/09/centos-subversion.html' title='CentOS 安装 Subversion'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-94621852406581121</id><published>2008-09-21T21:44:00.000-07:00</published><updated>2008-11-03T00:12:08.171-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>CentOS 安装  apache http server 2.2</title><content type='html'>1. download apache httpd source&lt;br /&gt;2. unzip it to /opt/httpd/httpd-2.2.9&lt;br /&gt;3. cd /opt/httpd-2.2.9, 执行 "./configure --prefix="/opt/httpd-2.2.9"&lt;br /&gt;4. make&lt;br /&gt;5. make install&lt;br /&gt;6. bin/apachectl start，然后打开首页 http://192.168.0.100/, it should show "it works"&lt;br /&gt;7. bin/apachectl stop&lt;br /&gt;&lt;br /&gt;你也可以编译安装到其他路径，但是你不可以移动已经编译好的apache httpd&lt;br /&gt;&lt;br /&gt;ref:&lt;br /&gt;http://httpd.apache.org/docs/2.0/install.html#configure&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;8. 作为服务安装在runlevel 3&lt;br /&gt;&lt;br /&gt;cd /etc/init.d&lt;br /&gt;vi appachehttpd， 加入以下内容&lt;br /&gt;#!/bin/bash&lt;br /&gt;#&lt;br /&gt;# Licensed to the Apache Software Foundation (ASF) under one or more&lt;br /&gt;# contributor license agreements.  See the NOTICE file distributed with&lt;br /&gt;# this work for additional information regarding copyright ownership.&lt;br /&gt;# The ASF licenses this file to You under the Apache License, Version 2.0&lt;br /&gt;# (the "License"); you may not use this file except in compliance with&lt;br /&gt;# the License.  You may obtain a copy of the License at&lt;br /&gt;#&lt;br /&gt;#     &lt;br /&gt;http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt;#&lt;br /&gt;# Unless required by applicable law or agreed to in writing, software&lt;br /&gt;# distributed under the License is distributed on an "AS IS" BASIS,&lt;br /&gt;# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt;# See the License for the specific language governing permissions and&lt;br /&gt;# limitations under the License.&lt;br /&gt;#&lt;br /&gt;#&lt;br /&gt;# Startup script for the Apache Web Server&lt;br /&gt;#&lt;br /&gt;# chkconfig: - 85 15&lt;br /&gt;# description: Apache is a World Wide Web server.  It is used to serve \&lt;br /&gt;#        HTML files and CGI.&lt;br /&gt;# processname: httpd&lt;br /&gt;# pidfile: /var/run/httpd.pid&lt;br /&gt;# config: /etc/httpd/conf/httpd.conf&lt;br /&gt;# Source function library.&lt;br /&gt;. /etc/rc.d/init.d/functions&lt;br /&gt;if [ -f /etc/sysconfig/httpd ]; then&lt;br /&gt;        . /etc/sysconfig/httpd&lt;br /&gt;fi&lt;br /&gt;# This will prevent initlog from swallowing up a pass-phrase prompt if&lt;br /&gt;# mod_ssl needs a pass-phrase from the user.&lt;br /&gt;INITLOG_ARGS=""&lt;br /&gt;# Set HTTPD=/usr/sbin/httpd.worker in /etc/sysconfig/httpd to use a server&lt;br /&gt;# with the thread-based "worker" MPM; BE WARNED that some modules may not&lt;br /&gt;# work correctly with a thread-based MPM; notably PHP will refuse to start.&lt;br /&gt;# Path to the apachectl script, server binary, and short-form for messages.&lt;br /&gt;apachectl=/usr/local/apache/bin/apachectl&lt;br /&gt;httpd=${HTTPD-/usr/local/apache/bin/httpd}&lt;br /&gt;prog=httpd&lt;br /&gt;RETVAL=0&lt;br /&gt;# check for 1.3 configuration&lt;br /&gt;check13 () {&lt;br /&gt;CONFFILE=/usr/local/apache/conf/httpd.conf&lt;br /&gt;GONE="(ServerType|BindAddress|Port|AddModule|ClearModuleList|"&lt;br /&gt;GONE="${GONE}AgentLog|RefererLog|RefererIgnore|FancyIndexing|"&lt;br /&gt;GONE="${GONE}AccessConfig|ResourceConfig)"&lt;br /&gt;if grep -Eiq "^[[:space:]]*($GONE)" $CONFFILE; then&lt;br /&gt;  echo&lt;br /&gt;  echo 1&gt;&amp;2 " Apache 1.3 configuration directives found"&lt;br /&gt;  echo 1&gt;&amp;2 " please read @docdir@/migration.html"&lt;br /&gt;  failure "Apache 1.3 config directives test"&lt;br /&gt;  echo&lt;br /&gt;  exit 1&lt;br /&gt;fi&lt;br /&gt;}&lt;br /&gt;# The semantics of these two functions differ from the way apachectl does&lt;br /&gt;# things -- attempting to start while running is a failure, and shutdown&lt;br /&gt;# when not running is also a failure.  So we just do it the way init scripts&lt;br /&gt;# are expected to behave here.&lt;br /&gt;start() {&lt;br /&gt;        echo -n $"Starting $prog: "&lt;br /&gt;        check13 || exit 1&lt;br /&gt;        daemon $httpd $OPTIONS&lt;br /&gt;        RETVAL=$?&lt;br /&gt;        echo&lt;br /&gt;        [ $RETVAL = 0 ] &amp;&amp; touch /var/lock/subsys/httpd&lt;br /&gt;        return $RETVAL&lt;br /&gt;}&lt;br /&gt;stop() {&lt;br /&gt;echo -n $"Stopping $prog: "&lt;br /&gt;killproc $httpd&lt;br /&gt;RETVAL=$?&lt;br /&gt;echo&lt;br /&gt;[ $RETVAL = 0 ] &amp;&amp; rm -f /var/lock/subsys/httpd /var/run/httpd.pid&lt;br /&gt;}&lt;br /&gt;reload() {&lt;br /&gt;echo -n $"Reloading $prog: "&lt;br /&gt;check13 || exit 1&lt;br /&gt;killproc $httpd -HUP&lt;br /&gt;RETVAL=$?&lt;br /&gt;echo&lt;br /&gt;}&lt;br /&gt;# See how we were called.&lt;br /&gt;case "$1" in&lt;br /&gt;  start)&lt;br /&gt;start&lt;br /&gt;;;&lt;br /&gt;  stop)&lt;br /&gt;stop&lt;br /&gt;;;&lt;br /&gt;  status)&lt;br /&gt;        status $httpd&lt;br /&gt;RETVAL=$?&lt;br /&gt;;;&lt;br /&gt;  restart)&lt;br /&gt;stop&lt;br /&gt;start&lt;br /&gt;;;&lt;br /&gt;  condrestart)&lt;br /&gt;if [ -f /var/run/httpd.pid ] ; then&lt;br /&gt;  stop&lt;br /&gt;  start&lt;br /&gt;fi&lt;br /&gt;;;&lt;br /&gt;  reload)&lt;br /&gt;        reload&lt;br /&gt;;;&lt;br /&gt;  graceful|help|configtest|fullstatus)&lt;br /&gt;$apachectl $@&lt;br /&gt;RETVAL=$?&lt;br /&gt;;;&lt;br /&gt;  *)&lt;br /&gt;echo $"Usage: $prog {start|stop|restart|condrestart|reload|status|fullstatus|graceful|help|configtest}"&lt;br /&gt;exit 1&lt;br /&gt;esac&lt;br /&gt;exit $RETVAL&lt;br /&gt;&lt;br /&gt;注意文件中有三处主要的地方需要修改下的：&lt;br /&gt;apachectl=/usr/local/apache/bin/apachectl&lt;br /&gt;httpd=${HTTPD-/usr/local/apache/bin/httpd}&lt;br /&gt;CONFFILE=/usr/local/apache/conf/httpd.conf&lt;br /&gt;&lt;br /&gt;然后&lt;br /&gt;chkconfig --add apachehttpd&lt;br /&gt;如果你要运行在多个runlevel，比如3和5&lt;br /&gt;chkconfig --level 35 apachehttpd on&lt;br /&gt;&lt;br /&gt;附各个run level:&lt;br /&gt;&lt;br /&gt;    * rc0.d - System Halted&lt;br /&gt;    * rc1.d - Single User Mode&lt;br /&gt;    * rc2.d - Single User Mode with Networking&lt;br /&gt;    * rc3.d - Multi-User Mode - boot up in text mode&lt;br /&gt;    * rc4.d - Not yet Defined&lt;br /&gt;    * rc5.d - Multi-User Mode - boot up in X Windows&lt;br /&gt;    * rc6.d - Shutdown &amp; Reboot&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;确认安装：&lt;br /&gt;cd /etc/rc.d/rc3.d&lt;br /&gt;you should see S85apachehttpd&lt;br /&gt;cat S85apachehttpd&lt;br /&gt;should show you the script&lt;br /&gt;&lt;br /&gt;reboot and you should see the web page&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-94621852406581121?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/94621852406581121/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=94621852406581121' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/94621852406581121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/94621852406581121'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/09/centos-apache-http-server-22.html' title='CentOS 安装  apache http server 2.2'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-2862552915354426591</id><published>2008-09-19T00:35:00.000-07:00</published><updated>2008-11-02T23:53:17.492-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>资源管理器里添加DOS和Cygwin Bash命令行到右键上下文菜单</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_MvCA5zIFrxg/SNNXUt3QCxI/AAAAAAAAFVQ/4YOPGMkSy94/s1600-h/1.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_MvCA5zIFrxg/SNNXUt3QCxI/AAAAAAAAFVQ/4YOPGMkSy94/s320/1.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5247634004237093650" /&gt;&lt;/a&gt;&lt;br /&gt;1. 添加DOS命令行到右键点击目录的上下文菜单&lt;br /&gt;添加注册表&lt;br /&gt;[HKEY_CLASSES_ROOT\Directory\shell\cmd]&lt;br /&gt;默认值：Open Command Line&lt;br /&gt;[HKEY_CLASSES_ROOT\Directory\shell\cmd\command]&lt;br /&gt;默认值：cmd.exe /k "cd %L"&lt;br /&gt;&lt;br /&gt;2. 添加Cygwin Bash命令行到右键点击目录的上下文菜单&lt;br /&gt;add c:\Tools\cygwin\bin to %PATH%&lt;br /&gt;然后 添加注册表&lt;br /&gt;[HKEY_CLASSES_ROOT\Directory\shell\cygwin]&lt;br /&gt;默认值：Open Cygwin Bash&lt;br /&gt;[HKEY_CLASSES_ROOT\Directory\shell\cygwin\command]&lt;br /&gt;默认值：c:\Tools\cygwin\bin\bash.exe --login -c "cd '%1' ; exec /bin/bash -rcfile ~/.bashrc"&lt;br /&gt;&lt;br /&gt;对于配置cygwin bash上下文菜单更多内容请看http://www.mindview.net/Etc/Cygwin/BashHere&lt;br /&gt;&lt;br /&gt;有了cygwin的一个主要好处是可以把windows dos 命令行和unix style的命令结合一起用，比如：&lt;br /&gt;$ netstat -nao | grep 139&lt;br /&gt;  TCP    16.158.65.15:139       0.0.0.0:0              LISTENING       4&lt;br /&gt;  TCP    192.168.116.1:139      0.0.0.0:0              LISTENING       4&lt;br /&gt;  TCP    192.168.199.1:139      0.0.0.0:0              LISTENING       4&lt;br /&gt;&lt;br /&gt;如果只用dos会很麻烦的事情，所以我现在在windows下也是用cgywin&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-2862552915354426591?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/2862552915354426591/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=2862552915354426591' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/2862552915354426591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/2862552915354426591'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/09/doscygwin-bash.html' title='资源管理器里添加DOS和Cygwin Bash命令行到右键上下文菜单'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_MvCA5zIFrxg/SNNXUt3QCxI/AAAAAAAAFVQ/4YOPGMkSy94/s72-c/1.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-926085989226864683</id><published>2008-09-18T23:41:00.000-07:00</published><updated>2008-11-03T00:12:50.747-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>这个缺乏诚信的年代，还是不喝牛奶为妙</title><content type='html'>完了，我喝了不知道多少光明的牛奶的，估计在我国也没有办法索赔了&lt;br /&gt;以后怎么办呢？还是不喝牛奶算了。不知道以后还能不能吃米饭，白菜？&lt;br /&gt;&lt;br /&gt;液态奶检出三聚氰胺的批次表&lt;br /&gt;公司  序号  生产企业  产品名称  规格型号  商标  生产日期／批次  三聚氰胺（mg/kg）&lt;br /&gt;蒙牛  1  蒙牛(武汉)友芝友乳业有限公司  核桃牛奶  200ml/袋  友芝友  20080910  0.765&lt;br /&gt;2  内蒙古蒙牛乳业(集团)股份有限公司  蒙牛高钙低脂牛奶  250ml/盒  蒙牛  2008.08.07  0.8&lt;br /&gt;3  内蒙古蒙牛乳业(集团)股份有限公司  全脂灭菌纯牛乳  250ml/盒  蒙牛  2008.09.01  1.0&lt;br /&gt;4  内蒙古蒙牛乳业(集团)股份有限公司  高钙低脂牛奶  250ml/盒  蒙牛  2008.08.01  1.5&lt;br /&gt;5  内蒙古蒙牛乳业(集团)股份有限公司  早餐奶(麦香味)  250ml/包  蒙牛  20080814  1.9&lt;br /&gt;6  内蒙古蒙牛乳业(集团)股份有限公司  蒙牛早餐奶  250ml/盒  蒙牛  2008.07.26/x  2.57&lt;br /&gt;7  内蒙古蒙牛乳业(集团)股份有限公司  妙点  250ml/盒  蒙牛  20080728/W206  3.17&lt;br /&gt;8  蒙牛乳业(北京)有限责任公司  木糖醇酸牛奶  2kg/瓶  蒙牛  20080806  3.52&lt;br /&gt;9  内蒙古蒙牛乳业(集团)股份有限公司  高钙低脂牛奶  243ml(250g)/袋  蒙牛  20080908/C206/GAfb  4.2&lt;br /&gt;10  蒙牛乳业(马鞍山)有限公司  蒙牛大粒果实酸牛奶  160克/盒  蒙牛  M20080903  6.8(A样)&lt;br /&gt;11  蒙牛乳业(马鞍山)有限公司  蒙牛大粒果实酸牛奶  160克/盒  蒙牛  M20080903  7(B样)&lt;br /&gt;伊利  1  济南伊利乳业有限责任公司  伊利芒果+黄桃酸牛奶  125g/盒  伊利  2008.09.07  0.69&lt;br /&gt;2  内蒙古伊利实业集团股份有限公司  酸牛奶(木瓜+甜橙)  125g/瓶  伊利  20080903  1.02&lt;br /&gt;3  内蒙古伊利实业集团股份有限公司  纯牛奶  220ml/袋  伊利  2008.09.13  2.2&lt;br /&gt;4  内蒙古伊利实业集团股份有限公司  脱脂奶  250ml/盒  伊利  20080820  2.9&lt;br /&gt;5  内蒙古伊利实业集团股份有限公司  纯牛奶  220ml/袋  伊利  20080905MIAC6  5.5&lt;br /&gt;6  内蒙古伊利实业集团股份有限公司  纯牛奶  242ml/袋  伊利  20080906/LIA09  8&lt;br /&gt;7  内蒙古伊利实业集团股份有限公司  高钙低脂奶  250ml/盒  伊利  20080819  8.4&lt;br /&gt;光明  1  北京光明健能乳业有限公司  光明酸牛奶(原味)  180g/袋  光明  2008.09.12  0.6&lt;br /&gt;2  武汉光明乳品有限公司  原味酸牛奶  180g/盒  光明  2008-09-13  3.41&lt;br /&gt;3  北京光明健能乳业有限公司  原味酸牛奶  100克/杯  光明  20080910A  3.5&lt;br /&gt;4  北京光明健能乳业有限公司  大颗果粒草莓酸奶  450克/盒  光明  20080902BC  4.8&lt;br /&gt;5  光明乳业有限责任公司  益生菌·优乳酪(原味)  190g/罐  光明  B20080908C  5.65&lt;br /&gt;6  北京光明健能乳业有限公司  优酪乳·酸牛奶(原味)  580克/瓶  光明  B20080909A  8.6&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-926085989226864683?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/926085989226864683/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=926085989226864683' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/926085989226864683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/926085989226864683'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/09/blog-post.html' title='这个缺乏诚信的年代，还是不喝牛奶为妙'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-5893620326424393276</id><published>2008-09-17T21:37:00.000-07:00</published><updated>2008-11-02T23:51:57.548-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSL'/><title type='text'>SSL 单向双向认证配置实例 - HP OpenView Service Manager和Tomcat</title><content type='html'>0. 如果没有openssl预装，那么你需要自己下载并编译,如果java -version是gnu的，那么要下载并安装Sun的JRE，最好是JDK&lt;br /&gt;1. 下载源代码http://www.openssl.org/source/&lt;br /&gt;2. 我使用CentOS5.2其他Linux/unix操作系统应该类似,以root身份登录&lt;br /&gt;3. 把source解压缩到安装路径，比如/opt/openssl&lt;br /&gt;4. 进入目录编译&lt;br /&gt;./config --prefix=/opt/openssl&lt;br /&gt;5. 安装&lt;br /&gt;make test&lt;br /&gt;make install&lt;br /&gt;&lt;br /&gt;配置openssl.cnf&lt;br /&gt;找到openssl.cnf&lt;br /&gt;[root@arsenal14 /]# find / -name openssl.cnf&lt;br /&gt;/etc/pki/tls/openssl.cnf&lt;br /&gt;然后加入你自己的country state等等：&lt;br /&gt;&lt;br /&gt;生成自签名的CA根证书&lt;br /&gt;首先生成CA的private key:&lt;br /&gt;[root@arsenal14 tmp]# openssl genrsa -des3 -out ca.pem 2048&lt;br /&gt;Generating RSA private key, 2048 bit long modulus&lt;br /&gt;...............................................................................................................................+++&lt;br /&gt;......................+++&lt;br /&gt;e is 65537 (0x10001)&lt;br /&gt;Enter pass phrase for ca.pem:&lt;br /&gt;Verifying - Enter pass phrase for ca.pem:&lt;br /&gt;&lt;br /&gt;然后用这个private key生成CA根证书&lt;br /&gt;[root@arsenal14 tmp]# openssl req -new -key ca.pem -x509 -days 1095 -out ca.cer&lt;br /&gt;Enter pass phrase for ca.pem:&lt;br /&gt;You are about to be asked to enter information that will be incorporated&lt;br /&gt;into your certificate request.&lt;br /&gt;What you are about to enter is what is called a Distinguished Name or a DN.&lt;br /&gt;There are quite a few fields but you can leave some blank&lt;br /&gt;For some fields there will be a default value,&lt;br /&gt;If you enter '.', the field will be left blank.&lt;br /&gt;-----&lt;br /&gt;Country Name (2 letter code) [CN]:&lt;br /&gt;State or Province Name (full name) [Shanghai]:&lt;br /&gt;Locality Name (eg, city) [Shanghai]:&lt;br /&gt;Organization Name (eg, company) [Hewlett Packard]:&lt;br /&gt;Organizational Unit Name (eg, section) []:GDCC-SMCi&lt;br /&gt;Common Name (eg, your name or your server's hostname) []:arsenal14.asiapacific.hpqcorp.net&lt;br /&gt;Email Address []:daniel.woo@hp.com&lt;br /&gt;注意机器名一定要是这台机器的FDQN&lt;br /&gt;&lt;br /&gt;到此为止，我们看看当前目录都有了哪些东西：&lt;br /&gt;[root@arsenal14 tmp]# ls&lt;br /&gt;ca.cer  CA根证书&lt;br /&gt;ca.pem  CA private key （重要！保密！）&lt;br /&gt;&lt;br /&gt;下面是一个真是用例，比如我们要把一台tomcat和一台HP OpenView ServiceManager（下文简称SM）建立mutal authentication.&lt;br /&gt;我们需要&lt;br /&gt;1. 建立一个CA&lt;br /&gt;2. 分别创建tomcat和SM的private/public key pair&lt;br /&gt;3. CA签发tomcat和SM的证书&lt;br /&gt;4. 导入CA证书到tomcat和SM的信任keystore&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;现在为了做mutual authentication我们还需要对SM和tomcat分别生成private key和证书，然后把证书通过CA签发。&lt;br /&gt;&lt;br /&gt;生成SM的private/public key pair[root@arsenal14 tmp]# keytool -genkey -alias smserver -keystore sm.jks&lt;br /&gt;Enter keystore password:&lt;br /&gt;Re-enter new password:&lt;br /&gt;What is your first and last name?&lt;br /&gt;  [Unknown]:  tsmcivm8&lt;br /&gt;What is the name of your organizational unit?&lt;br /&gt;  [Unknown]:&lt;br /&gt;What is the name of your organization?&lt;br /&gt;  [Unknown]:&lt;br /&gt;What is the name of your City or Locality?&lt;br /&gt;  [Unknown]:&lt;br /&gt;What is the name of your State or Province?&lt;br /&gt;  [Unknown]:&lt;br /&gt;What is the two-letter country code for this unit?&lt;br /&gt;  [Unknown]:  CN&lt;br /&gt;Is CN=tsmcivm8, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CN correct?&lt;br /&gt;  [no]:  y&lt;br /&gt;&lt;br /&gt;Enter key password for &lt;sm&gt;&lt;br /&gt;        (RETURN if same as keystore password):&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;生成tomcat的private/public key pair&lt;br /&gt;&lt;br /&gt;[root@arsenal14 tmp]# keytool -genkey -alias tomcat -keystore tomcat.jks&lt;br /&gt;Enter keystore password:&lt;br /&gt;Re-enter new password:&lt;br /&gt;What is your first and last name?&lt;br /&gt;  [Unknown]:  danielnc6400.asiapacific.hpqcorp.net&lt;br /&gt;What is the name of your organizational unit?&lt;br /&gt;  [Unknown]:&lt;br /&gt;What is the name of your organization?&lt;br /&gt;  [Unknown]:&lt;br /&gt;What is the name of your City or Locality?&lt;br /&gt;  [Unknown]:&lt;br /&gt;What is the name of your State or Province?&lt;br /&gt;  [Unknown]:&lt;br /&gt;What is the two-letter country code for this unit?&lt;br /&gt;  [Unknown]:  CN&lt;br /&gt;Is CN=danielnc6400.asiapacific.hpqcorp.net, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CN correct?&lt;br /&gt;  [no]:  y&lt;br /&gt;&lt;br /&gt;Enter key password for &lt;tomcat&gt;&lt;br /&gt;        (RETURN if same as keystore password):&lt;br /&gt;Re-enter new password:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;现在多了两个包含private/public key的keystore:&lt;br /&gt;sm.jks  和 tomcat.jks&lt;br /&gt;&lt;br /&gt;我们从中导出两个待签发的证书（只包含public key）&lt;br /&gt;sm_req.crs  和 tomcat_req.crs&lt;br /&gt;&lt;br /&gt;[root@arsenal14 tmp]# keytool -certreq -alias smserver -keystore sm.jks -file sm_req.crs&lt;br /&gt;Enter key store password: password&lt;br /&gt;&lt;br /&gt;[root@arsenal14 tmp]# keytool -certreq -alias tomcat -keystore tomcat.jks -file tomcat_req.crs&lt;br /&gt;Enter key store password: password&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;然后我们要用CA根证书签发出一个SM的证书，发布给其他系统&lt;br /&gt;[root@arsenal14 tmp]# openssl x509 -req -days 365 -in sm_req.crs -CA ca.cer -CAkey ca.pem -CAcreateserial -out sm.cer&lt;br /&gt;Signature ok&lt;br /&gt;subject=/CN=tsmcivm8/O=HP/OU=GDCC-SMCi/L=Shanghai/ST=Shanghai/C=CN&lt;br /&gt;Getting CA Private Key&lt;br /&gt;Enter pass phrase for ca.pem:&lt;br /&gt;&lt;br /&gt;然后我们要用CA根证书签发出一个tomcat的证书，发布给其他系统&lt;br /&gt;[root@arsenal14 tmp]# openssl x509 -req -days 365 -in tomcat_req.crs -CA ca.cer -CAkey ca.pem -CAcreateserial -out tomcat.cer&lt;br /&gt;Signature ok&lt;br /&gt;subject=/CN=danielnc6400.asiapacific.hpqcorp.net/O=HP/OU=GDCC-SMCi/L=Shanghai/ST=Shanghai/C=CN&lt;br /&gt;Getting CA Private Key&lt;br /&gt;Enter pass phrase for ca.pem:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;现在又多了两个文件，两个sign好的证书sm.cer 和 tomcat.cer&lt;br /&gt;&lt;br /&gt;我们有了这些文件:&lt;br /&gt;ca.cer  根证书&lt;br /&gt;ca.pem  根证书private key&lt;br /&gt;ca.srl  &lt;br /&gt;sm.cer  已经签名的sm证书&lt;br /&gt;sm.jks  sm server keystore&lt;br /&gt;sm.pem  sm private key &lt;br /&gt;sm_req.crs  没用了&lt;br /&gt;tomcat.cer  已经签名的tomcat证书&lt;br /&gt;tomcat.jks  tomcat server keystore&lt;br /&gt;tomcat.pem  tomcat private key&lt;br /&gt;tomcat_req.crs 没用了&lt;br /&gt;&lt;br /&gt;现在把signed好的证书导回keystore&lt;br /&gt;但是之前你必须把CA的根证书导入&lt;br /&gt;[root@arsenal14 tmp]# keytool -cacert -file ca.cer -keystore sm.jks&lt;br /&gt;[root@arsenal14 tmp]# keytool -cacert -file ca.cer -keystore tomcat.jks&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;然后才能导入CA签发好的证书&lt;br /&gt;keytool -import -file sm.cer -keystore sm.jks -alias smserver&lt;br /&gt;keytool -import -file tocmat.cer -keystore tomcat.jks -alias tomcat&lt;br /&gt;&lt;br /&gt;先测试tomcat的单向服务器认证&lt;br /&gt;把tomcat.jks复制到tomcat/conf下&lt;br /&gt;打开tomcat/conf/server.xml,更改https的connector配置&lt;br /&gt;先测试单向tomcat服务器端验证（不要求客户上传验证客户端证书)&lt;br /&gt; &lt;Connector port="443" protocol="HTTP/1.1" URIEncoding="UTF-8"&lt;br /&gt;           minSpareThreads="2" maxSpareThreads="15"&lt;br /&gt;           enableLookups="true" disableUploadTimeout="true" &lt;br /&gt;           acceptCount="100"  maxThreads="100"&lt;br /&gt;           scheme="https" secure="true" SSLEnabled="true"&lt;br /&gt;           keystoreFile="C:/Projects/Tomcat6SM/conf/tomcat.jks" keyAlias="tomcat" keystorePass="password"&lt;br /&gt;           clientAuth="false" sslProtocol="TLS"/&gt;&lt;br /&gt;&lt;br /&gt;启动tomcat, log应该有&lt;br /&gt;2008-9-18 23:24:38 org.apache.coyote.http11.Http11Protocol start&lt;br /&gt;信息: Starting Coyote HTTP/1.1 on http-443&lt;br /&gt;&lt;br /&gt;从FireFox3这个时候访问https://danielnc6400.asiapacific.hpqcorp.net你应该看到这个内容&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_MvCA5zIFrxg/SNJzzfF0nQI/AAAAAAAAFU4/6HY8KQfeGa0/s1600-h/not+trusted.JPG"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_MvCA5zIFrxg/SNJzzfF0nQI/AAAAAAAAFU4/6HY8KQfeGa0/s320/not+trusted.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5247383844196490498" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;这是因为你不信任这个证书，下一步我们需要把根证书导入FireFox的trust keystore&lt;br /&gt;Tools-&gt;Option-&gt;Advanced-&gt;Encryption-&gt;View Certificates-&gt;authorities-&gt;Import&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_MvCA5zIFrxg/SNJ1Sk1st9I/AAAAAAAAFVA/sLhyBy85CyQ/s1600-h/import.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_MvCA5zIFrxg/SNJ1Sk1st9I/AAAAAAAAFVA/sLhyBy85CyQ/s320/import.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5247385477827049426" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;导入之后你可以查看所有已经导入的证书&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_MvCA5zIFrxg/SNJ1n9LHIII/AAAAAAAAFVI/BwC49ag0q8g/s1600-h/view+list.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_MvCA5zIFrxg/SNJ1n9LHIII/AAAAAAAAFVI/BwC49ag0q8g/s320/view+list.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5247385845136564354" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;IE直接访问会弹出对话框问你是否信任&lt;br /&gt;IE证书导入更简单，直接double click ca.cer安装根证书&lt;br /&gt;然后再次打开这个URL你会发现IE不在有任何提示，因为IE信任这个根证，所以也信任根证书签发的tomcat的证书。&lt;br /&gt;&lt;br /&gt;接下来配置SM的单向server认证&lt;br /&gt;复制sm.jks 到SM安装路径的RUN/security目录下&lt;br /&gt;修改sm.ini如下（sm -helpssl可以显示所有参数的意义）&lt;br /&gt;# SSL configuration&lt;br /&gt;ssl:0&lt;br /&gt;ssl_reqClientAuth:0&lt;br /&gt;sslConnector:1&lt;br /&gt;httpsPort:13443&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# Certificates&lt;br /&gt;truststoreFile:security/sm.jks&lt;br /&gt;truststorePass:password&lt;br /&gt;keystoreFile:security/sm.jks&lt;br /&gt;keystorePass:password&lt;br /&gt;&lt;br /&gt;重新启动服务&lt;br /&gt;&lt;br /&gt;然后到客户端打开Windows-&gt;Preferences-&gt;HP Service Manager-&gt;Security&lt;br /&gt;把包含CA根证书的那个keystore全路径写在CA Certificate file里.这里是为了能让客户端验证服务器上的证书是否有效。不过奇怪的是这里没有输入访问keystore的密码的地方,eclipse client怎么打开keystore还是个迷。:-)&lt;br /&gt;&lt;br /&gt;然后新建一个链接，hostname一定要填写域名，第二页要勾上Use SSL Encryption，现在你就可以连接上SM了&lt;br /&gt;&lt;br /&gt;这个单向配置都没有问题了，我们来尝试把SM和tomcat做mutual authentication&lt;br /&gt;由于双方trust keystore里面都信任ca.cer所以理论上讲现在是自动支持的。&lt;br /&gt;&lt;br /&gt;打开SM做个实验，打开Script Library&lt;br /&gt;创建个JS脚本&lt;br /&gt;var url = "https://danielnc6400.asiapacific.hpqcorp.net/index.html";&lt;br /&gt;var headers = new Array();&lt;br /&gt;//headers.push("Authorization: Basic " + base64Encode("administrator:admblabla"));&lt;br /&gt;var resp = doHTTPRequest( "GET", url, headers, null, 10, 10, 10 );&lt;br /&gt;print(resp);&lt;br /&gt;&lt;br /&gt;执行脚本输出是：&lt;br /&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"&lt;br /&gt;   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;This is a test&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;br /&gt;结束&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-5893620326424393276?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/5893620326424393276/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=5893620326424393276' title='3 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5893620326424393276'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/5893620326424393276'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/09/ssl.html' title='SSL 单向双向认证配置实例 - HP OpenView Service Manager和Tomcat'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_MvCA5zIFrxg/SNJzzfF0nQI/AAAAAAAAFU4/6HY8KQfeGa0/s72-c/not+trusted.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-4532301295414510129</id><published>2008-09-17T06:45:00.000-07:00</published><updated>2008-11-02T23:55:05.150-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Database'/><title type='text'>MySQL Master-Slave Replication</title><content type='html'>&lt;div&gt;MySQL Master-Slave Replication&lt;br /&gt;&lt;br /&gt;Offical reference&lt;br /&gt;http://dev.mysql.com/doc/refman/5.0/en/replication.html&lt;br /&gt;&lt;br /&gt;简化步骤：&lt;br /&gt;&lt;b&gt;1. download &lt;/b&gt;&lt;br /&gt;    mySQL for centOS5 (RHEL5)建议intel cc编译的这个版本，建议下载5.1版本,6.0的falcon性能并不比innoDB好&lt;br /&gt;    &lt;i&gt;&lt;span style="font-size:85%;"&gt;Linux (non RPM, Intel C/C++ compiled, glibc-2.3) downloads&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;span style="font-family:Verdana, Arial, Helvetica, sans-serif;font-size:85%;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;;"&gt;配置默认网关：/etc/sysconfig/network&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;配置dhcp或者静态IP在/etc/network-scripts/ifcfg-eth0, 1, 2...&lt;br /&gt;配置dns hostname在/etc/resolv.conf&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. 如果已经有了mysql那么先 rpm -ev mysql&lt;br /&gt;然后关闭iptables防火墙&lt;/b&gt;&lt;br /&gt;&lt;div style="margin-left: 40px; color: rgb(102, 102, 102);"&gt;[root@arsenal14 mysqlM]# chkconfig --list iptables&lt;br /&gt;iptables        0:off   1:off   2:on    3:on    4:on    5:on    6:off&lt;br /&gt;[root@arsenal14 mysqlM]# service iptables save&lt;br /&gt;Saving firewall rules to /etc/sysconfig/iptables:          [  OK  ]&lt;br /&gt;[root@arsenal14 mysqlM]# service iptables stop&lt;br /&gt;Flushing firewall rules:                                   [  OK  ]&lt;br /&gt;Setting chains to policy ACCEPT: filter                    [  OK  ]&lt;br /&gt;Unloading iptables modules:                                [  OK  ]&lt;br /&gt;[root@arsenal14 mysqlM]# chkconfig  iptables off&lt;br /&gt;[root@arsenal14 mysqlM]# chkconfig --list iptables&lt;br /&gt;iptables        0:off   1:off   2:off   3:off   4:off   5:off   6:off&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;3. 创建用户&lt;/b&gt;&lt;br /&gt;groupadd mysql&lt;br /&gt;useradd -g mysql mysql&lt;br /&gt;passwd mysql&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. 解开压缩包然后复制到三份，master-slave-slave&lt;/b&gt;&lt;br /&gt;cp -r mysql* /opt/mysqlM&lt;br /&gt;cp -r mysql* /opt/mysqlS0&lt;br /&gt;cp -r mysql* /opt/mysqlS1&lt;br /&gt;到/opt下执行&lt;br /&gt;&lt;span style="font-family: Verdana;"&gt;chown -R mysql *&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: Verdana;"&gt;chgrp -R mysql *&lt;/span&gt;&lt;br /&gt;到mysqlM,和另外两个数据库下执行&lt;br /&gt;scripts/mysql_install_db --user=mysql&lt;br /&gt;&lt;span style="font-family: Verdana;"&gt;chown -R mysql data&lt;/span&gt;&lt;br /&gt;chgrp -R mysql data&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5. 启动主数据库&lt;/b&gt;&lt;br /&gt;./bin/mysqld_safe --user=mysql &amp;amp;&lt;br /&gt;然后测试数据库状态：&lt;br /&gt;[root@arsenal14 mysqlM]#  bin/mysqladmin version&lt;br /&gt;bin/mysqladmin  Ver 8.42 Distrib 5.1.26-rc, for redhat-linux-gnu on i686&lt;br /&gt;Copyright (C) 2000-2006 MySQL AB&lt;br /&gt;This software comes with ABSOLUTELY NO WARRANTY. This is free software,&lt;br /&gt;and you are welcome to modify and redistribute it under the GPL license&lt;br /&gt;&lt;br /&gt;Server version          5.1.26-rc&lt;br /&gt;Protocol version        10&lt;br /&gt;Connection              Localhost via UNIX socket&lt;br /&gt;UNIX socket             /tmp/mysql.sock&lt;br /&gt;Uptime:                 1 min 13 sec&lt;br /&gt;&lt;br /&gt;Threads: 1  Questions: 1  Slow queries: 0  Opens: 15  Flush tables: 1  Open tables: 8  Queries per second avg: 0.13&lt;br /&gt;关闭数据库 ./mysqladmin shutdown&lt;br /&gt;创建配置文件，把support-files里的my-medium.conf复制到mysqlM/my.cnf&lt;br /&gt;更改一些参数,比如&lt;br /&gt;port=4306&lt;br /&gt;socket=/tmp/mysqlM.sock&lt;br /&gt;collation_server=utf8_unicode_ci&lt;br /&gt;character_set_server=utf8&lt;br /&gt;打开innoDB的配置等&lt;br /&gt;&lt;br /&gt;然后用这个命令行启动mysql:&lt;br /&gt;&lt;code class="option"&gt;    mysqld_safe --defaults-file=/opt/mysqlM/my.cnf --user=mysql&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;用ping看看数据库状态&lt;br /&gt;&lt;div style="margin-left: 40px;"&gt;&lt;span style="color: rgb(153, 153, 153);"&gt;[root@arsenal14 mysqlM]# bin/mysqladmin --defaults-file=/opt/mysqlM/my.cnf  ping&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(153, 153, 153);"&gt;mysqld is alive&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;如果你需要用root通过远程客户端登录，你需要enable mysql 的root remote login&lt;br /&gt;&lt;div style="margin-left: 40px; background-color: rgb(255, 255, 255); color: rgb(102, 102, 102);"&gt;[root@arsenal14 mysqlM]# bin/mysql  --defaults-file=/opt/mysqlM/my.cnf&lt;br /&gt;Welcome to the MySQL monitor.  Commands end with ; or \g.&lt;br /&gt;Your MySQL connection id is 7&lt;br /&gt;Server version: 5.1.26-rc-log MySQL Community Server (GPL)&lt;br /&gt;&lt;br /&gt;Type 'help;' or '\h' for help. Type '\c' to clear the buffer.&lt;br /&gt;&lt;br /&gt;mysql&gt; GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'password' WITH GRANT OPTION;&lt;br /&gt;Query OK, 0 rows affected (0.00 sec)&lt;br /&gt;&lt;br /&gt;mysql&gt; exit&lt;br /&gt;Bye&lt;br /&gt;&lt;/div&gt;然后你就可以用toad for mysql了&lt;br /&gt;&lt;div id="rf7-" style="padding: 1em 0pt; text-align: left;"&gt;&lt;img style="width: 763px; height: 540px;" src="http://docs.google.com/File?id=d82bt7r_64f8s8rhdf_b" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;6. 配置两个slave数据库&lt;/b&gt;&lt;br /&gt;[root@arsenal14 mysqlM]# cp my.cnf ../mysqlS0&lt;br /&gt;[root@arsenal14 mysqlM]# cp my.cnf ../mysqlS1&lt;br /&gt;然后只要改端口和sock文件不要重复，innoDB数据文件路径不重复，pid file 不重复就可以了&lt;br /&gt;建议改成&lt;br /&gt;&lt;div style="margin-left: 40px; color: rgb(102, 102, 102);"&gt;port=5306&lt;br /&gt;socket          = /tmp/mysqlS.sock&lt;br /&gt;innodb_data_home_dir = /opt/mysqlS0/data/&lt;br /&gt;innodb_data_file_path = ibdata1:10M:autoextend&lt;br /&gt;innodb_log_group_home_dir = /opt/mysqlS0/data/&lt;br /&gt;pid-file=/opt/mysqlS0/pid&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;port            = 6306&lt;br /&gt;socket          = /tmp/mysqlS1.sock&lt;br /&gt;innodb_data_home_dir = /opt/mysqlS1/data/&lt;br /&gt;innodb_data_file_path = ibdata1:10M:autoextend&lt;br /&gt;innodb_log_group_home_dir = /opt/mysqlS1/data/&lt;br /&gt;pid-file=/opt/mysqlS1/pid&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;检查是否可以启动，查看err日志&lt;br /&gt;[root@arsenal14 mysqlS0]# bin/mysqld_safe --defaults-file=/opt/mysqlS0/my.cnf --user=mysql &amp;amp;&lt;br /&gt;&lt;br /&gt;[root@arsenal14 mysqlS1]# bin/mysqld_safe --defaults-file=/opt/mysqlS1/my.cnf --user=mysql &amp;amp;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;7. 准备知识&lt;/b&gt;&lt;br /&gt;现在才开始真正配置master-slave，有些基础知识要知道&lt;br /&gt;    1. master-slave之间使用master的数据库日志来让slave知道master发生了什么&lt;br /&gt;    2. 除了master-slave模式，mySQL现在cluster也是免费的了&lt;br /&gt;    3. mySQL proxy 是一个代理组件，上面可以运行mysql load balancer&lt;br /&gt;    4. mysql load balancer的好处：&lt;br /&gt;        4.1 自动排除掉同步失败或者落后的slave&lt;br /&gt;        4.2 修复后同步正常的slave可以自动加入&lt;br /&gt;        4.3 把读取分布转发到集群内任何一台服务器&lt;br /&gt;        4.4 写不可以分发&lt;br /&gt;        4.5 保持各个数据库链接数平均&lt;br /&gt;&lt;br /&gt;&lt;b&gt;8. 配置主数据库&lt;/b&gt;&lt;br /&gt;先关闭数据库 ./mysqladmin shutdown&lt;br /&gt;在master上创建同步用的用户,我用了root偷懒&lt;br /&gt;&lt;br /&gt;接下来要master打开日志，并标识一个ID&lt;br /&gt;[mysqld]&lt;br /&gt;log-bin=mysql-bin&lt;br /&gt;server-id=1&lt;br /&gt;&lt;code class="literal"&gt;innodb_flush_log_at_trx_commit=1&lt;/code&gt;&lt;br /&gt;sync_binlog=1&lt;br /&gt;&lt;span style="font-size:85%;"&gt;binlog-do-db=test&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;b&gt;9. 配置一个slave数据库&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;指定一个id&lt;br /&gt;[mysqld]&lt;br /&gt;server-id=2&lt;br /&gt;master-host     =arsenal14.asiapacific.hpqcorp.net&lt;br /&gt;master-user     =mysqlrep&lt;br /&gt;master-password =password&lt;br /&gt;master-port     =4036&lt;br /&gt;replicate-do-db=test&lt;br /&gt;&lt;br /&gt;&lt;code class="literal"&gt;&lt;b&gt;10.配置master-slave&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;/div&gt;如果master已经有了数据，那么你要停止master的数据操作，先dump到slave保持一致才能开始replication&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-left: 40px; color: rgb(102, 102, 102);"&gt;mysql&gt; FLUSH TABLES WITH READ LOCK;&lt;br /&gt;mysql&gt; SHOW MASTER STATUS;&lt;br /&gt;+------------------+----------+--------------+------------------+&lt;br /&gt;| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |&lt;br /&gt;+------------------+----------+--------------+------------------+&lt;br /&gt;| mysql-bin.000010  |      106 |              |                  |&lt;br /&gt;+------------------+----------+--------------+------------------+&lt;br /&gt;1 row in set (0.01 sec)&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;这里bin 日志是000006，偏移量是106，记下来这个数据&lt;br /&gt;&lt;br /&gt;在命令行下导出master的数据&lt;br /&gt;./bin/mysqldump --defaults-file=/opt/mysqlM/my.cnf --all-databases --lock-all-tables &gt;dbdump.db&lt;br /&gt;然后倒入到slave中。&lt;br /&gt;./bin/mysql -h master &lt;&gt;最后在slave配置目标master&lt;br /&gt;这里应该是&lt;br /&gt;mysql&gt; STOP SLAVE;&lt;br /&gt;Query OK, 0 rows affected (0.01 sec)&lt;br /&gt;mysql&gt; change master to  master_host='arsenal14.asiapacific.hpqcorp.net', master_port=4306,  master_user='mysql', master_password='password', &lt;span style="color: rgb(255, 0, 0);"&gt;master_log_file='mysql-bin.000010', master_log_pos=106;&lt;/span&gt;&lt;br /&gt;Query OK, 0 rows affected (0.02 sec)&lt;br /&gt;&lt;br /&gt;好了，重新启动slave，然后对master新建一个表看看,不行的话要改配置需要先删除data/maseter.info，然后再执行上面的sql&lt;br /&gt;然后你可以看到第三条数据是自动复制过来的，貌似create table没有正常复制过来，自己创建了table之后数据就复制过来了，钱两行因为没有表所以丢失了&lt;br /&gt;mysql复制有两种，statement和record的，DDL比如创建表同步这个问题需要看一下&lt;br /&gt;我又重新做了测试，这次DDL也可以同步了，数据也是正确的。刚才的问题一直没有再次重现，比较奇怪。&lt;br /&gt;&lt;div id="tfuc" style="padding: 1em 0pt; text-align: left;"&gt;&lt;img style="width: 409px; height: 263px;" src="http://docs.google.com/File?id=d82bt7r_65dm7nfmfp_b" /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-4532301295414510129?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/4532301295414510129/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=4532301295414510129' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4532301295414510129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/4532301295414510129'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/09/mysql-master-slave-replication.html' title='MySQL Master-Slave Replication'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-405440534643619078.post-1509496896587256515</id><published>2008-01-14T19:40:00.000-08:00</published><updated>2008-11-02T23:55:41.617-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='其它'/><title type='text'>远程桌面超出了最大允许连接数的解决办法</title><content type='html'>&lt;span style="font-size:100%;"&gt;运行 mstsc /v:IP /console&lt;br /&gt;就可以连接到远程主机的的0会话。&lt;br /&gt;然后更改远程桌面配置：&lt;br /&gt;Start-&gt;control pannel-&gt;administrative tools-&gt;terminal services configuration-&gt;RDP-Tcp&lt;br /&gt;修改end a disconnected session的时间&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/405440534643619078-1509496896587256515?l=danielywoo.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://danielywoo.blogspot.com/feeds/1509496896587256515/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=405440534643619078&amp;postID=1509496896587256515' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/1509496896587256515'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/405440534643619078/posts/default/1509496896587256515'/><link rel='alternate' type='text/html' href='http://danielywoo.blogspot.com/2008/01/blog-post.html' title='远程桌面超出了最大允许连接数的解决办法'/><author><name>Daniel Wu</name><uri>http://www.blogger.com/profile/17755999827557861281</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://1.bp.blogspot.com/_MvCA5zIFrxg/SOddP6Y8OxI/AAAAAAAAFV4/R64bL0mJABA/S220/100_1798.JPG'/></author><thr:total>0</thr:total></entry></feed>
