﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-yg2362-随笔分类-数据库</title><link>http://www.cppblog.com/yg2362/category/20221.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 06 Dec 2012 15:16:46 GMT</lastBuildDate><pubDate>Thu, 06 Dec 2012 15:16:46 GMT</pubDate><ttl>60</ttl><item><title>大区中分配玩家唯一ID的办法(续)</title><link>http://www.cppblog.com/yg2362/archive/2012/12/06/196061.html</link><dc:creator>梨树阳光</dc:creator><author>梨树阳光</author><pubDate>Thu, 06 Dec 2012 15:01:00 GMT</pubDate><guid>http://www.cppblog.com/yg2362/archive/2012/12/06/196061.html</guid><wfw:comment>http://www.cppblog.com/yg2362/comments/196061.html</wfw:comment><comments>http://www.cppblog.com/yg2362/archive/2012/12/06/196061.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yg2362/comments/commentRss/196061.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yg2362/services/trackbacks/196061.html</trackback:ping><description><![CDATA[<span style="font-family: Verdana; font-size: 10pt; ">&nbsp; &nbsp; &nbsp; 之前因为时间匆忙，大区里产生唯一ID只是简单写了一个方法，今天趁着有时间，把里面一些过程写出来，也算是个总结。</span><br /><span style="font-family: Verdana; font-size: 10pt; ">&nbsp; &nbsp; &nbsp; 首先说说需求，现在游戏里数据库为了分散热点，都采用分表方式，以前那种一张表AUTO_INCREMENT的方式显然不行了，那么将唯一ID放到业务进程里可行吗？这个也不好，因为现在后台都是多个部署，每个进程可以保证ID唯一，但是存到数据库的时候就可能重复，所以我们还是得在DB上做文章。</span><br /><span style="font-family: Verdana; font-size: 10pt; ">&nbsp; &nbsp; &nbsp; 因此就有了上篇文章的解决方案，单独一张表，</span><span style="line-height: 19px; font-family: Verdana; font-size: 10pt; ">这张表的作用就是专门产生唯一ID，而且为多个业务提供唯一ID，多进程情况下也不需要锁表，效率比较高，是不是很像设计模式里的工厂。接着我来分析下这张表。我们再来看看表结构和用法<br /></span><span style="background-color: #ffffff; font-family: Verdana; font-size: 13px; line-height: 19px; ">create table SeqTab&nbsp;</span><span style="background-color: #ffffff; font-family: Verdana; font-size: 13px; line-height: 19px; ">(&nbsp;<br /></span>&nbsp; &nbsp;<span style="background-color: #ffffff; font-family: Verdana; font-size: 13px; line-height: 19px; ">iSeqNo bigint(20) not null default 0,</span><span style="font-family: Verdana; ">&nbsp; &nbsp;//表示唯一ID</span><span style="background-color: #ffffff; font-family: Verdana, Arial; font-size: 13px; line-height: 19px; "><br /></span><span style="background-color: #ffffff; font-family: Verdana; font-size: 13px; line-height: 19px; ">&nbsp; &nbsp;iSeqType int(11) not null default 0,</span><span style="font-family: Verdana; ">&nbsp; &nbsp;//表示业务ID</span><span style="background-color: #ffffff; font-family: Verdana, Arial; font-size: 13px; line-height: 19px; "><br /></span><span style="font-family: Verdana; ">&nbsp; &nbsp;primary key(iSeqNo,iSeqType)</span><span style="background-color: #ffffff; font-family: Verdana; font-size: 13px; line-height: 19px; ">);<br /></span>&nbsp; &nbsp;<span style="font-family: Verdana; font-size: 13px; line-height: 19px; background-color: #ffffff; ">insert into SeqTab values(0,1);</span><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; &nbsp; //初始化，假设一个业务编号为1</span><span style="font-family: Verdana, Arial; font-size: 13px; line-height: 19px; background-color: #ffffff; "><br /></span>&nbsp; &nbsp;<span style="font-family: Verdana; font-size: 13px; line-height: 19px; background-color: yellow; ">update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=1;<br /></span>&nbsp; &nbsp;<span style="font-family: Verdana; font-size: 13px; line-height: 19px; background-color: yellow; ">select last_insert_id();</span><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//这两句是获取一个业务的唯一ID，供业务进程使用。</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp;其实说到这张表，关键是说LAST_INSERT_ID()这个方法。它有两种形式LAST_INSERT_ID()，LAST_INSERT_ID(expr)。</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp;这个东西的特点是，1.属于每个CONNECTION，CONNECTION之间相互不会影响 2.不属于某个具体的表 3.返回最后一次INSERT AUTO_INCREMENT的值 4.假如以此使用INSERT插入 &nbsp; &nbsp;多行数据，只返回第一行数据产生的值 5.如果你UPDATE某个</span><span style="font-family: Verdana; ">AUTO_INCREMENT的值，不会影响</span><span style="font-family: Verdana; ">LAST_INSERT_ID()返回值</span><br />&nbsp; &nbsp;<span style="font-family: Verdana; ">LAST_INSERT_ID(expr)与</span><span style="font-family: Verdana; ">LAST_INSERT_ID()稍有不同，首先它返回expr的值，其次它的返回值会记录在</span><span style="font-family: Verdana; ">LAST_INSERT_ID()。</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp;我们这里主要使用到了第一和第二个特点，每个进程并发执行</span><span style="font-family: Verdana; font-size: 13px; line-height: 19px; background-color: #ffff00; ">update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=1;<span style="background-color: #ffffff; ">,</span><span style="background-color: #ffffff; ">就获取属于进程自己的iSeqNo并且记录在&nbsp;</span></span><span style="font-family: Verdana; font-size: 13px; line-height: 19px; background-color: #ffff00; "><span style="background-color: #ffffff; ">LAST_INSERT_ID中，通过第二句取出该值。</span></span>&nbsp; &nbsp;<br /><span style="font-family: Verdana; ">&nbsp; &nbsp; 接着我们在比较下其他一些办法。</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; 第一种是直接一张表，里面有一个ID字段，设置成AUTO_INCREMENT。这个的问题是每个业务ID不是连续的，是离散的。</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; 第二种是使用GUID或者UUID，但是这个问题我个人觉得是效率上的差异，字符串没有数字的效率好，另外数字ID今后也可以拼接一些区信息，之后跨区的时候可以方便获取对象是哪个区的.</span><img src ="http://www.cppblog.com/yg2362/aggbug/196061.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yg2362/" target="_blank">梨树阳光</a> 2012-12-06 23:01 <a href="http://www.cppblog.com/yg2362/archive/2012/12/06/196061.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>大区中分配玩家唯一ID的办法</title><link>http://www.cppblog.com/yg2362/archive/2012/12/05/196002.html</link><dc:creator>梨树阳光</dc:creator><author>梨树阳光</author><pubDate>Wed, 05 Dec 2012 03:54:00 GMT</pubDate><guid>http://www.cppblog.com/yg2362/archive/2012/12/05/196002.html</guid><wfw:comment>http://www.cppblog.com/yg2362/comments/196002.html</wfw:comment><comments>http://www.cppblog.com/yg2362/archive/2012/12/05/196002.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/yg2362/comments/commentRss/196002.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yg2362/services/trackbacks/196002.html</trackback:ping><description><![CDATA[<div><span style="font-family: Verdana; ">Mysql中除了使用auto_increment字段作为自增序号以外 还有另外一种办法 可以提供唯一序列号并且可以由一张表完成对多个序列</span></div><div><span style="font-family: Verdana; ">要求的满足。</span></div><div></div><div><span style="font-family: Verdana; ">大致如下：</span></div><div><span style="font-family: Verdana; ">1.创建一个简单表&nbsp;</span></div><div><span style="font-family: Verdana; ">&nbsp; &nbsp;create table SeqTab</span></div><div><span style="font-family: Verdana; ">&nbsp; &nbsp;( iSeqNo int not null default 0,</span></div><div><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp;iSeqType int not null default 0);</span></div><div></div><div><span style="font-family: Verdana; ">2.插入一调记录</span></div><div><span style="font-family: Verdana; ">&nbsp; &nbsp;insert into SeqTab values(0,13); &nbsp;// 0表示SeqNo初始值，13为SeqType，同一张表可对多个应用提供Seq序列服务</span></div><div></div><div><span style="font-family: Verdana; ">3.不管多进程 or 单进程对SeqTab表同时进行访问，使用一下方法获取序列号</span></div><div><span style="font-family: Verdana; ">&nbsp; &nbsp;1st-&gt;update SeqTab set iSeqNo=last_insert_id(iSeqNo+1) where iSeqType=13;</span></div><div><span style="font-family: Verdana; ">&nbsp; &nbsp;2nd-&gt;select last_insert_id();</span></div><div></div><div><span style="font-family: Verdana; ">4.因多进程对SeqTab同时访问，进行update SeqTab set iSeqNo=last_insert_id(iSeqNo+1);时，数据库保证了update的事务</span></div><div><span style="font-family: Verdana; ">完整性，且last_insert_id()是与当前mysql连接相关的，所以多进程同时使用时，也不会冲突。<br /><br /></span></div><img src ="http://www.cppblog.com/yg2362/aggbug/196002.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yg2362/" target="_blank">梨树阳光</a> 2012-12-05 11:54 <a href="http://www.cppblog.com/yg2362/archive/2012/12/05/196002.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>mysql中文乱码解析</title><link>http://www.cppblog.com/yg2362/archive/2012/11/26/195711.html</link><dc:creator>梨树阳光</dc:creator><author>梨树阳光</author><pubDate>Mon, 26 Nov 2012 11:56:00 GMT</pubDate><guid>http://www.cppblog.com/yg2362/archive/2012/11/26/195711.html</guid><wfw:comment>http://www.cppblog.com/yg2362/comments/195711.html</wfw:comment><comments>http://www.cppblog.com/yg2362/archive/2012/11/26/195711.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/yg2362/comments/commentRss/195711.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yg2362/services/trackbacks/195711.html</trackback:ping><description><![CDATA[&nbsp; &nbsp; &nbsp; <span style="font-family: Verdana; ">相信大家在开发后台的过程中都遇到过中文乱码的问题，今天我就来讲讲其中的原因。</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 我这建了3张表，test_latin1,test_utf8,test_gbk,表结构如下</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp;&nbsp;+-------+----------+------+-----+---------+-------+</span><div><span style="font-family: Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| Field | Type &nbsp; &nbsp; | Null | Key | Default | Extra |</span></div><div><span style="font-family: Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+-------+----------+------+-----+---------+-------+</span></div><div><span style="font-family: Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| name &nbsp;| char(32) | YES &nbsp;| &nbsp; &nbsp; | NULL &nbsp; &nbsp;| &nbsp; &nbsp; &nbsp; |</span></div><div><span style="font-family: Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+-------+----------+------+-----+---------+-------+</span><br /><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 我的前端是gbk的编码</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 执行下面的语句</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; set names 'latin1'</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; insert into test_latin1 set name='王';('王'字是GBK编码)</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; select name from test_latin1;</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 结果是否为乱码？</span><br /><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp;&nbsp;执行下面的语句</span></div><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; set names 'gbk'</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; insert into test_latin1 set name='王';('王'字是GBK编码)</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; select name from test_latin1;</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 结果是否为乱码？</span><br /><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp;&nbsp;执行下面的语句</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; set names 'latin1'</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; insert into test_utf8 set name='王';('王'字是GBK编码)</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; select name from&nbsp;test_utf8&nbsp;;</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 结果是否为乱码？</span><br /><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 我们举个例子，假设一个汉字的字符编码为0xFFFF，它在屏幕上能够正常显示，如果汉字存入数据库的时候和从数据库中取出的时候，编码一致，那么它肯定不是乱码。反过来，如果输出的时候是乱码，那么它肯定被转码了，至于为什么被转码了，我们得看看mysql里面做了什么(mysql难道会把无码片变成了有码片？)</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 首先mysql里面有2个概念，一个叫character set,一个叫collation。我们先说说character set。字符集就是数字，英文字符，汉字等编码格式，我们常见的是utf8,gbk,gb2312。mysql里面比较复杂，有4个东西跟它有关，分别是character_set_client，character_set_connection，character_set_database，character_set_results。set names (latin1)其实就是character_set_client=latin1，character_set_connection=latin1，character_set_results=latin1，它的流程是character_set_client ==&gt;&nbsp;character_set_connection ==&gt; Table Character ==&gt;&nbsp;character_set_results。</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 我们按照上面的流程，来分析第一个问题。</span><br /><span style="font-family: Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set names 'latin1'----执行了character_set_client=latin1，character_set_connection=latin1，character_set_results=latin1;</span><br /><span style="font-family: Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insert into test_latin1 set name='王';这句话，mysql做了什么事呢？首先，character_set_client,它会把王字的编码当成latin1的编码传递给character_set_connection（此时不会转码），character_set_connection会把编码传递给Table Character,因为表本身是latin1，所以此时也不需要转码，select name from test_latin1;mysql会把test_latin1中的编码传递给前端，此时也不需要转码，所以，走个流程下来，我们输入的是什么编码，输出的还是相同的编码，因此，第一个问题的答案是不会是乱码。我画个流程图latin1==&gt;latin1==&gt;latin1==&gt;latin1,没有转码的过程</span><br />&nbsp; &nbsp; &nbsp;&nbsp;<br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 我们在来看第二个问题。</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp;&nbsp;set names 'test_gbk'----执行了character_set_client=gbk，character_set_connection=gbk，character_set_results=gbk;</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp;&nbsp;insert into test_latin1 set name='王';character_set_client,它会把王字的编码当成gbk的编码传递给character_set_connection（此时不会转码）,character_set_connection会把编码传递给Table Character,因为表是lanti1的编码格式，这个过程的时候就会进行转码，但是latin1的字符集小于gbk的字符集，所以它会找不到对应字符的编码，此时会以？代替。select name from test_latin1，此时会从latin1转码成gbk,但是此时latin1已经是错误的数据了，所以得到的gbk编码也是错误的了。流程gbk==&gt;gbk==&gt;latin1==&gt;gbk,其中gbk==&gt;latin1出了问题，我们select出来的数据也就不可能是输入时候的数据了。因此，这个问题的答案是乱码。</span><br /><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 第三个。</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp;&nbsp;set names 'test_latin1'</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp;&nbsp;insert into test_utf8 set name='王';character_set_client,它会把王字的编码当成latin1的编码传递给character_set_connection（此时不会转码）,character_set_connection会把编码传递给Table Character,此时表是utf8的格式，因此会进行转码，latin1==&gt;utf8,因为utf8的字符集&gt;latin1字符集，因此，转码正常。select name from test_utf8;会从utf8转码成latin1,此时可以转码成功，因此我们最终得到的和输入的时候是一致的，因此答案不是乱码。流程latin1==&gt;latin1==&gt;utf8==&gt;latin1,从小的字符集到大的字符集再到小的字符集，转码是不会有问题的。</span><br /><span style="font-family: Verdana; ">&nbsp; &nbsp; &nbsp; 屁话了这么多，无非想告诉大家一个万精油方法，</span><strong style="font-family: Verdana; font-size: 14.399999618530273px; line-height: 23.200000762939453px; background-color: #ffffff; ">表创建的字符集和set&nbsp;names都设置成同一个字符集，就基本可以满足输入数据不会在转换过程中失真</strong><span style="font-family: Verdana; font-size: 14.399999618530273px; line-height: 23.200000762939453px; background-color: #ffffff; ">，也就是说输入是什么，输出就是什么。</span><strong style="font-family: Verdana; font-size: 14.399999618530273px; line-height: 23.200000762939453px; background-color: #ffffff; ">建议有中文的都设置成utf8字符集，一劳永逸。</strong><img src ="http://www.cppblog.com/yg2362/aggbug/195711.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yg2362/" target="_blank">梨树阳光</a> 2012-11-26 19:56 <a href="http://www.cppblog.com/yg2362/archive/2012/11/26/195711.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>