存档:2008年八月

分表的思想和实现

八月 29, 2008 | mysql, 网站架构 | RSS 2.0

作者:heiyeluren (黑夜路人)
博客:http://blog.csdn.net/heiyeshuwu
时间:2007-01-19 01:44:20
一、概述
分表是个目前算是比较炒的比较流行的概念,特别是在大负载的情况下,分表是一个良好分散数据库压力的好方法。
首先要了解为什么要分表,分表的好处是什么。我们先来大概了解以下一个数据库执行SQL的过程:
接收到SQL –> 放入SQL执行队列 –> 使用分析器分解SQL –> 按照分析结果进行数据的提取或者修改 –> 返回处理结果
当然,这个流程图不一定正确,这只是我自己主观意识上这么我认为。那么这个处理过程当中,最容易出现问题的是什么?就是说,如果前一个SQL没有执行完毕的话,后面的SQL是不会执行的,因为为了保证数据的完整性,必须对数据表文件进行锁定,包括共享锁和独享锁两种锁定。共享锁是在锁定的期间,其它线程也可以访问这个数据文件,但是不允许修改操作,相应的,独享锁就是整个文件就是归一个线程所有,其它线程无法访问这个数据文件。一般MySQL中最快的存储引擎MyISAM,它是基于表锁定的,就是说如果一锁定的话,那么整个数据文件外部都无法访问,必须等前一个操作完成后,才能接收下一个操作,那么在这个前一个操作没有执行完成,后一个操作等待在队列里无法执行的情况叫做阻塞,一般我们通俗意义上叫做“锁表”。
锁表直接导致的后果是什么?就是大量的SQL无法立即执行,必须等队列前面的SQL全部执行完毕才能继续执行。这个无法执行的SQL就会导致没有结果,或者延迟严重,影响用户体验。
特别是对于一些使用比较频繁的表,比如SNS系统中的用户信息表、论坛系统中的帖子表等等,都是访问量大很大的表,为了保证数据的快速提取返回给用户,必须使用一些处理方式来解决这个问题,这个就是我今天要聊到的分表技术。
分表技术顾名思义,就是把若干个存储相同类型数据的表分成几个表分表存储,在提取数据的时候,不同的用户访问不同的表,互不冲突,减少锁表的几率。比如,目前保存用户分表有两个表,一个是user_1表,还有一个是 user_2 表,两个表保存了不同的用户信息,user_1保存了前10万的用户信息,user_2保存了后10万名用户的信息,现在如果同时查询用户 heiyeluren1 和 heiyeluren2这个两个用户,那么就是分表从不同的表提取出来,减少锁表的可能。
我下面要讲述的两种分表方法我自己都没有实验过,不保证准确能用,只是提供一个设计思路。下面关于分表的例子我假设是在一个贴吧系统的基础上来进行处理和构建的。(如果没有用过贴吧的用户赶紧Google一下)
二、基于基础表的分表处理
这个基于基础表的分表处理方式大致的思想就是:一个主要表,保存了所有的基本信息,如果某个项目需要找到它所存储的表,那么必须从这个基础表中查找出对应的表名等项目,好直接访问这个表。如果觉得这个基础表速度不够快,可以完全把整个基础表保存在缓存或者内存中,方便有效的查询。
我们基于贴吧的情况,构建假设如下的3张表:
1. 贴吧版块表: 保存贴吧中版块的信息
2. 贴吧主题表:保存贴吧中版块中的主题信息,用于浏览
3. 贴吧回复表:保存主题的原始内容和回复内容
“贴吧版块表”包含如下字段:
版块ID       board_id          int(10)
版块名称    board_name      char(50)
子表ID       table_id            smallint(5)
产生时间    created             datetime
“贴吧主题表”包含如下字段:
主题ID          topic_id        int(10)
主题名称        topic_name     char(255)
版块ID          board_id          int(10)
创建时间       created           datetime
“贴吧回复表”的字段如下:
回复ID        reply_id           int(10)
回复内容      reply_text        text
主题ID        topic_id           int(10)
版块ID        board_id         int(10)
创建时间      created            datetime
那么上面保存了我们整个贴吧中的表结构信息,三个表对应的关系是:
版块 –> 多个主题
主题 –> 多个回复
那么就是说,表文件大小的关系是:
版块表文件 < 主题表文件 < 回复表文件
所以基本可以确定需要对主题表和回复表进行分表,已增加我们数据检索查询更改时候的速度和性能。
看了上面的表结构,会明显发现,在“版块表”中保存了一个”table_id”字段,这个字段就是用于保存一个版块对应的主题和回复都是分表保存在什么表里的。
比如我们有一个叫做“PHP”的贴吧,board_id是1,子表ID也是1,那么这条记录就是:
board_id | board_name | table_id | created
1 | PHP | 1 | 2007-01-19 00:30:12
相应的,如果我需要提取“PHP”吧里的所有主题,那么就必须按照表里保存的table_id来组合一个存储了主题的表名称,比如我们主题表的前缀是“topic_”,那么组合出来“PHP”吧对应的主题表应该是:“topic_1”,那么我们执行:
SELECT * FROM topic_1 WHERE board_id = 1 ORDER BY topic_id DESC LIMIT 10
这样就能够获取这个主题下面回复列表,方便我们进行查看,如果需要查看某个主题下面的回复,我们可以继续使用版块表中保存的“table_id”来进行查询。比如我们回复表的前缀是“reply_”,那么就可以组合出“PHP”吧的ID为1的主题的回复:
SELECT * FROM reply_1 WHERE topic_id = 1 ORDER BY reply_id DESC LIMIT 10
这里,我们能够清晰的看到,其实我们这里使用了基础表,基础表就是我们的版块表。那么相应的,肯定会说:基础表的数据量大了以后如何保证它的速度和效率?
当然,我们就必须使得这个基础表保持最好的速度和性能,比如,可以采用MySQL的内存表来存储,或者保存在内存当中,比如Memcache之类的内存缓存等等,可以按照实际情况来进行调整。
一般基于基础表的分表机制在SNS、交友、论坛等Web2.0网站中是个比较不错的解决方案,在这些网站中,完全可以单独使用一个表来来保存基本标识和目标表之间的关系。使用表保存对应关系的好处是以后扩展非常方便,只需要增加一个表记录。
优势】增加删除节点非常方便,为后期升级维护带来很大便利
劣势】需要增加表或者对某一个表进行操作,还是无法离开数据库,会产生瓶颈
三、基于Hash算法的分表处理
我们知道Hash表就是通过某个特殊的Hash算法计算出的一个值,这个值必须是惟一的,并且能够使用这个计算出来的值查找到需要的值,这个叫做哈希表。
我们在分表里的hash算法跟这个思想类似:通过一个原始目标的ID或者名称通过一定的hash算法计算出数据存储表的表名,然后访问相应的表。
继续拿上面的贴吧来说,每个贴吧有版块名称和版块ID,那么这两项值是固定的,并且是惟一的,那么我们就可以考虑通过对这两项值中的一项进行一些运算得出一个目标表的名称。
现在假如我们针对我们这个贴吧系统,假设系统最大允许1亿条数据,考虑每个表保存100万条记录,那么整个系统就不超过100个表就能够容纳。按照这个标准,我们假设在贴吧的版块ID上进行hash,获得一个key值,这个值就是我们的表名,然后访问相应的表。
我们构造一个简单的hash算法:
function get_hash($id){
     $str = bin2hex($id);
     $hash = substr($str, 0, 4);
     if (strlen($hash)<4){
         $hash = str_pad($hash, 4, “0″);
     }
     return $hash;
}
算法大致就是传入一个版块ID值,然后函数返回一个4位的字符串,如果字符串长度不够,使用0进行补全。
比如:get_hash(1),输出的结果是“3100”,输入:get_hash(23819),得到的结果是:3233,那么我们经过简单的跟表前缀组合,就能够访问这个表了。那么我们需要访问ID为1的内容时候哦,组合的表将是:topic_3100、reply_3100,那么就可以直接对目标表进行访问了。
当然,使用hash算法后,有部分数据是可能在同一个表的,这一点跟hash表不同,hash表是尽量解决冲突,我们这里不需要,当然同样需要预测和分析表数据可能保存的表名。
如果需要存储的数据更多,同样的,可以对版块的名字进行hash操作,比如也是上面的二进制转换成十六进制,因为汉字比数字和字母要多很多,那么重复几率更小,但是可能组合成的表就更多了,相应就必须考虑一些其它的问题。
归根结底,使用hash方式的话必须选择一个好的hash算法,才能生成更多的表,然数据查询的更迅速。
优点hash算法直接得出目标表名称,效率很高】通过
劣势】扩展性比较差,选择了一个hash算法,定义了多少数据量,以后只能在这个数据量上跑,不能超过过这个数据量,可扩展性稍差
四、其它问题
1. 搜索问题
现在我们已经进行分表了,那么就无法直接对表进行搜索,因为你无法对可能系统中已经存在的几十或者几百个表进行检索,所以搜索必须借助第三方的组件来进行,比如Lucene作为站内搜索引擎是个不错的选择。
2. 表文件问题
我们知道MySQL的MyISAM引擎每个表都会生成三个文件,*.frm、*.MYD、*.MYI三个文件,分表用来保存表结构、表数据和表索引。Linux下面每个目录下的文件数量最好不要超过1000个,不然检索数据将更慢,那么每个表都会生成三个文件,相应的如果分表超过300个表,那么将检索非常慢,所以这时候就必须再进行分,比如在进行数据库的分离。
使用基础表,我们可以新增加一个字段,用来保存这个表保存在什么数据。使用Hash的方式,我们必须截取hash值中第几位来作为数据库的名字。这样,完好的解决这个问题。
五、总结
在大负载应用当中,数据库一直是个很重要的瓶颈,必须要突破,本文讲解了两种分表的方式,希望对很多人能够有启发的作用。当然,本文代码和设想没有经过任何代码测试,所以无法保证设计的完全准确实用,具体还是需要读者在使用过程当中认真分析实施。
文章写的比较匆忙,质量可能无法保证,遇到错误,不要见怪,欢迎提出批评指教,谢谢~~~~!

没有评论 »

php优化

八月 29, 2008 | php, 软件工程/编程技巧/设计模式 | RSS 2.0

学健同学周末做了个tech talk,题目就是php优化,避开了简单的代码优化,基本依照php脚本到前端显示的流程来描述优化点,已经相关的优化方法。
php优化
图中灰色部分是可以优化的地方。
1.从php脚本到opcode(operation code)可以采用zend加速器
2.可以利用APC 和eAccelerator等实现将opcode缓存,避免每次都去解释php脚本:
参考下面两篇:如何利用APC缓冲PHP脚本来提高服务器性能 给PHP加速,eAccelerator配置和使用指南
3.前端缓存,主要利用内存来减少I/O,加快速度。一台机器可以利用mdbm,多台前端机可以利用memcache。
4.页面压缩,gzip或者mod_deflate 减少带宽,提高cpu利用率。
5.apache mod_expire 控制图片、页面等静态内容的过期时间。yahoo有专门的yimg服务器,存放图片、css、js,这些内容不变,第一次访问后缓存在用户本地机器,提高速度。

没有评论 »

别人的经验

八月 19, 2008 | 网站架构 | RSS 2.0

主要是针对数据量很大,和并发访问量高的时候

经验一:
在开发过程中,我们经常会写
SELECT * FROM table WHERE 1 ORDER BY xxx DESC LIMIT 0,10
这样的语句用来分页

在有完美索引的情况 对xxx建立索引
前面几页会很快,但如果数据量达到100万级以后,我们查询最后一页
SELECT * FROM table WHERE 1 ORDER BY xxx DESC LIMIT 999990,10

这句执行就会很慢,同时有多人访问服务器就会掉 (这里不考虑缓存,因为内容更新太快,有时候缓存了达不到数据的更新的要求)

但如果我们把
SELECT * FROM table WHERE 1 ORDER BY xxx DESC LIMIT 999990,10
换成
SELECT * FROM table WHERE 1 ORDER BY xxx ASC LIMIT 0,10
这两个的MYSQL执行时间可是大大的不一样 当然要注意把这样取出来的结果用PHP重新排序一下

取得的一样是最后一页的数据,当然最中间的两页有部分数据一样

这时候最慢的只是最中间的部分,相对而言,访问最中间的人还是很少的

经验二:
例如论坛帖子列表的显示:
一般是SELECT * FROM table ORDER BY is_top DESC ,post_time DESC LIMIT 0,10这样的分页

两个order by 的执行是非常慢的,哪怕你有再好的索引,

我们的处理办法是 把is_top的数据CACHE住,毕竟is_top的数据量有限,更新这个缓存也容易

然后SQL一样是SELECT * FROM table ORDER BY post_time DESC LIMIT {$num},{$num2}

注意这个$num2 是减掉is_top的数量后的一个值,$num是is_top的数量

当然还要考虑is_top的数据量是不是有好几页,当前页的值是不是都在cache里面

经验三:

SELECT * FROM table ORDER BY RAND() LIMIT 100 这个ORDER BY RAND() 是非常慢的 能不用尽量不要用

处理办法是
1.用PHP生成数组后,然后用SELECT * FROM table WHERE id IN() WHERE IN 也比这个order by rand()快的多
2.如果数量信息不太重多,就用SELECT * FROM table WHERE 1 LIMIT 500 多取点数据,然后用php 处理数组

没有评论 »

utf8详解

八月 18, 2008 | 数学 | RSS 2.0

 

UTF-8编码的详细讲解

这篇文章说明了在 POSIX 系统 (Linux,Unix) 上使用 Unicode/UTF-8 所需要的信息. 在将来不远的几年里, Unicode 已经很接近于取代 ASCII 与 Latin-1 编码的位置了. 它不仅允许你处理处理事实上存在于地球上的任何语言文字, 而且提供了一个全面的数学与技术符号集, 因此可以简化科学信息交换.

UTF-8 编码提供了一种简便而向后兼容的方法, 使得那种完全围绕 ASCII 设计的操作系统, 比如 Unix, 也可以使用 Unicode. UTF-8 就是 Unix, Linux 已经类似的系统使用 Unicode 的方式. 现在是你了解它的时候了.

什么是 UCS 和 ISO 10646?

国际标准 ISO 10646 定义了 通用字符集 (Universal Character Set, UCS). UCS 是所有其他字符集标准的一个超集. 它保证与其他字符集是双向兼容的. 就是说, 如果你将任何文本字符串翻译到 UCS格式, 然后再翻译回原编码, 你不会丢失任何信息.

UCS 包含了用于表达所有已知语言的字符. 不仅包括拉丁语,希腊语, 斯拉夫语,希伯来语,阿拉伯语,亚美尼亚语和乔治亚语的描述, 还包括中文, 日文和韩文这样的象形文字, 以及 平假名, 片假名, 孟加拉语, 旁遮普语果鲁穆奇字符(Gurmukhi), 泰米尔语, 印.埃纳德语(Kannada), Malayalam, 泰国语, 老挝语, 汉语拼音(Bopomofo), Hangul, Devangari, Gujarati, Oriya, Telugu 以及其他数也数不清的语. 对于还没有加入的语言, 由于正在研究怎样在计算机中最好地编码它们, 因而最终它们都将被加入. 这些语言包括 Tibetian, 高棉语, Runic(古代北欧文字), 埃塞俄比亚语, 其他象形文字, 以及各种各样的印-欧语系的语言, 还包括挑选出来的艺术语言比如 Tengwar, Cirth 和克林贡语(Klingon). UCS 还包括大量的图形的, 印刷用的, 数学用的和科学用的符号, 包括所有由 TeX, Postscript, MS-DOS,MS-Windows, Macintosh, OCR 字体, 以及许多其他字处理和出版系统提供的字符.

ISO 10646 定义了一个 31 位的字符集. 然而, 在这巨大的编码空间中, 迄今为止只分配了前 65534 个码位 (0×0000 到 0xFFFD). 这个 UCS 的 16位子集称为 基本多语言面 (Basic Multilingual Plane, BMP). 将被编码在 16 位 BMP 以外的字符都属于非常特殊的字符(比如象形文字), 且只有专家在历史和科学领域里才会用到它们. 按当前的计划, 将来也许再也不会有字符被分配到从 0×000000 到 0×10FFFF 这个覆盖了超过 100 万个潜在的未来字符的 21 位的编码空间以外去了. ISO 10646-1 标准第一次发表于 1993 年, 定义了字符集与 BMP 中内容的架构. 定义 BMP 以外的字符编码的第二部分 ISO 10646-2 正在准备中, 但也许要过好几年才能完成. 新的字符仍源源不断地加入到 BMP 中, 但已经存在的字符是稳定的且不会再改变了.

UCS 不仅给每个字符分配一个代码, 而且赋予了一个正式的名字. 表示一个 UCS 或 Unicode 值的十六进制数, 通常在前面加上 “U+”, 就象 U+0041 代表字符”拉丁大写字母A”. UCS 字符 U+0000 到 U+007F 与 US-ASCII(ISO 646) 是一致的, U+0000 到 U+00FF 与 ISO 8859-1(Latin-1) 也是一致的. 从 U+E000 到 U+F8FF, 已经 BMP 以外的大范围的编码是为私用保留的.

什么是组合字符?

UCS里有些编码点分配给了 组合字符.它们类似于打字机上的无间隔重音键. 单个的组合字符不是一个完整的字符. 它是一个类似于重音符或其他指示标记, 加在前一个字符后面. 因而, 重音符可以加在任何字符后面. 那些最重要的被加重的字符, 就象普通语言的正字法(orthographies of common languages)里用到的那种, 在 UCS 里都有自己的位置, 以确保同老的字符集的向后兼容性. 既有自己的编码位置, 又可以表示为一个普通字符跟随一个组合字符的被加重字符, 被称为预作字符(precomposed characters). UCS 里的预作字符是为了同没有预作字符的旧编码, 比如 ISO 8859, 保持向后兼容性而设的. 组合字符机制允许在任何字符后加上重音符或其他指示标记, 这在科学符号中特别有用, 比如数学方程式和国际音标字母, 可能会需要在一个基本字符后组合上一个或多个指示标记.

组合字符跟随着被修饰的字符. 比如, 德语中的元音变音字符 (”拉丁大写字母A 加上分音符”), 既可以表示为 UCS 码 U+00C4 的预作字符, 也可以表示成一个普通 “拉丁大写字母A” 跟着一个”组合分音符”:U+0041 U+0308 这样的组合. 当需要堆叠多个重音符, 或在一个基本字符的上面和下面都要加上组合标记时, 可以使用多个组合字符. 比如在泰国文中, 一个基本字符最多可加上两个组合字符.

什么是 UCS 实现级别?

不是所有的系统都需要支持象组合字符这样的 UCS 里所有的先进机制. 因此 ISO 10646 指定了下列三种实现级别:  

级别1  

不支持组合字符和 Hangul Jamo 字符 (一种特别的, 更加复杂的韩国文的编码, 使用两个或三个子字符来编码一个韩文音节)  

级别2  

类似于级别1, 但在某些文字中, 允许一列固定的组合字符 (例如, 希伯来文, 阿拉伯文, Devangari, 孟加拉语, 果鲁穆奇语, Gujarati, Oriya, 泰米尔语, Telugo, 印.埃纳德语, Malayalam, 泰国语和老挝语). 如果没有这最起码的几个组合字符, UCS 就不能完整地表达这些语言.  

级别3  

支持所有的 UCS 字符, 例如数学家可以在任意一个字符上加上一个 tilde(颚化符号,西班牙语字母上面的~)或一个箭头(或两者都加).  

什么是 Unicode?

历史上, 有两个独立的, 创立单一字符集的尝试. 一个是国际标准化组织(ISO)的 ISO 10646 项目, 另一个是由(一开始大多是美国的)多语言软件制造商组成的协会组织的 Unicode 项目. 幸运的是, 1991年前后, 两个项目的参与者都认识到, 世界不需要两个不同的单一字符集. 它们合并双方的工作成果, 并为创立一个单一编码表而协同工作. 两个项目仍都存在并独立地公布各自的标准, 但 Unicode 协会和 ISO/IEC JTC1/SC2 都同意保持 Unicode 和 ISO 10646 标准的码表兼容, 并紧密地共同调整任何未来的扩展.

那么 Unicode 和 ISO 10646 不同在什么地方?

Unicode 协会公布的 Unicode 标准 严密地包含了 ISO 10646-1 实现级别3的基本多语言面. 在两个标准里所有的字符都在相同的位置并且有相同的名字.

Unicode 标准额外定义了许多与字符有关的语义符号学, 一般而言是对于实现高质量的印刷出版系统的更好的参考. Unicode 详细说明了绘制某些语言(比如阿拉伯语)表达形式的算法, 处理双向文字(比如拉丁与希伯来文混合文字)的算法和 排序与字符串比较 所需的算法, 以及其他许多东西.

另一方面, ISO 10646 标准, 就象广为人知的 ISO 8859 标准一样, 只不过是一个简单的字符集表. 它指定了一些与标准有关的术语, 定义了一些编码的别名, 并包括了规范说明, 指定了怎样使用 UCS 连接其他 ISO 标准的实现, 比如 ISO 6429 和 ISO 2022. 还有一些与 ISO 紧密相关的, 比如 ISO 14651 是关于 UCS 字符串排序的.

考虑到 Unicode 标准有一个易记的名字, 且在任何好的书店里的 Addison-Wesley 里有, 只花费 ISO 版本的一小部分, 且包括更多的辅助信息, 因而它成为使用广泛得多的参考也就不足为奇了. 然而, 一般认为, 用于打印 ISO 10646-1 标准的字体在某些方面的质量要高于用于打印 Unicode 2.0的. 专业字体设计者总是被建议说要两个标准都实现, 但一些提供的样例字形有显著的区别. ISO 10646-1 标准同样使用四种不同的风格变体来显示表意文字如中文, 日文和韩文 (CJK), 而 Unicode 2.0 的表里只有中文的变体. 这导致了普遍的认为 Unicode 对日本用户来说是不可接收的传说, 尽管是错误的.

什么是 UTF-8?

首先 UCS 和 Unicode 只是分配整数给字符的编码表. 现在存在好几种将一串字符表示为一串字节的方法. 最显而易见的两种方法是将 Unicode 文本存储为 2 个 或 4 个字节序列的串. 这两种方法的正式名称分别为 UCS-2 和 UCS-4. 除非另外指定, 否则大多数的字节都是这样的(Bigendian convention). 将一个 ASCII 或 Latin-1 的文件转换成 UCS-2 只需简单地在每个 ASCII 字节前插入 0×00. 如果要转换成 UCS-4, 则必须在每个 ASCII 字节前插入三个 0×00.

在 Unix 下使用 UCS-2 (或 UCS-4) 会导致非常严重的问题. 用这些编码的字符串会包含一些特殊的字符, 比如 ’0’ 或 ’/’, 它们在 文件名和其他 C 库函数参数里都有特别的含义. 另外, 大多数使用 ASCII 文件的 UNIX 下的工具, 如果不进行重大修改是无法读取 16 位的字符的. 基于这些原因, 在文件名, 文本文件, 环境变量等地方, UCS-2 不适合作为 Unicode 的外部编码.

在 ISO 10646-1 Annex R 和 RFC 2279 里定义的 UTF-8 编码没有这些问题. 它是在 Unix 风格的操作系统下使用 Unicode 的明显的方法.

UTF-8 有一下特性:  

UCS 字符 U+0000 到 U+007F (ASCII) 被编码为字节 0×00 到 0×7F (ASCII 兼容). 这意味着只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 两种编码方式下是一样的.  

所有 >U+007F 的 UCS 字符被编码为一个多个字节的串, 每个字节都有标记位集. 因此, ASCII 字节 (0×00-0×7F) 不可能作为任何其他字符的一部分.  

表示非 ASCII 字符的多字节串的第一个字节总是在 0xC0 到 0xFD 的范围里, 并指出这个字符包含多少个字节. 多字节串的其余字节都在 0×80 到 0xBF 范围里. 这使得重新同步非常容易, 并使编码无国界, 且很少受丢失字节的影响.  

可以编入所有可能的 231个 UCS 代码  

UTF-8 编码字符理论上可以最多到 6 个字节长, 然而 16 位 BMP 字符最多只用到 3 字节长.  

Bigendian UCS-4 字节串的排列顺序是预定的.  

字节 0xFE 和 0xFF 在 UTF-8 编码中从未用到.  

下列字节串用来表示一个字符. 用到哪个串取决于该字符在 Unicode 中的序号.

U-00000000 – U-0000007F: 0xxxxxxx  

U-00000080 – U-000007FF: 110xxxxx 10xxxxxx  

U-00000800 – U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx  

U-00010000 – U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx  

U-00200000 – U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  

U-04000000 – U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  

xxx 的位置由字符编码数的二进制表示的位填入. 越靠右的 x 具有越少的特殊意义. 只用最短的那个足够表达一个字符编码数的多字节串. 注意在多字节串中, 第一个字节的开头”1″的数目就是整个串中字节的数目.

例如: Unicode 字符 U+00A9 = 1010 1001 (版权符号) 在 UTF-8 里的编码为:

11000010 10101001 = 0xC2 0xA9

而字符 U+2260 = 0010 0010 0110 0000 (不等于) 编码为:

11100010 10001001 10100000 = 0xE2 0×89 0xA0

这种编码的官方名字拼写为 UTF-8, 其中 UTF 代表 UCS Transformation Format. 请勿在任何文档中用其他名字 (比如 utf8 或 UTF_8) 来表示 UTF-8, 当然除非你指的是一个变量名而不是这种编码本身.

什么编程语言支持 Unicode?

在大约 1993 年之后开发的大多数现代编程语言都有一个特别的数据类型, 叫做 Unicode/ISO 10646-1 字符. 在 Ada95 中叫 Wide_Character, 在 Java 中叫 char.

ISO C 也详细说明了处理多字节编码和宽字符 (wide characters) 的机制, 1994 年 9 月 Amendment 1 to ISO C 发表时又加入了更多. 这些机制主要是为各类东亚编码而设计的, 它们比处理 UCS 所需的要健壮得多. UTF-8 是 ISO C 标准调用多字节字符串的编码的一个例子, wchar_t 类型可以用来存放 Unicode 字符.

在 Linux 下该如何使用 Unicode?

在 UTF-8 之前, 不同地区的 Linux 用户使用各种各样的 ASCII 扩展. 最普遍的欧洲编码是 ISO 8859-1 和 ISO 8859-2, 希腊编码 ISO 8859-7, 俄国编码 KOI-8, 日本编码 EUC 和 Shift-JIS, 等等. 这使得文件的交换非常困难, 且应用软件必须特别关心这些编码的不同之处.

最终, Unicode 将取代所有这些编码, 主要通过 UTF-8 的形式. UTF-8 将应用在  

文本文件 (源代码, HTML 文件, email 消息, 等等)

文件名  

标准输入与标准输出, 管道  

环境变量  

剪切与粘贴选择缓冲区  

telnet, modem 和到终端模拟器的串口连接  

以及其他地方以前用ASCII来表示的字节串  

在 UTF-8 模式下, 终端模拟器, 比如 xterm 或 Linux console driver, 将每次按键转换成相应的 UTF-8 串, 然后发送到前台进程的 stdin 里. 类似的, 任何进程在 stdout 上的输出都将发送到终端模拟器, 在那里用一个 UTF-8 解码器进行处理, 之后再用一种 16 位的字体显示出来.

只有在功能完善的多语言字处理器包里才可能有完全的 Unicode 功能支持. 而广泛用在 Linux 里用于取代 ASCII 和其他 8 位字符集的方案则要简单得多. 第一步, Linux 终端模拟器和命令行工具将只是转变到 UTF-8. 这意味着只用到 级别1 的 ISO 10646-1 实现 (没有组合字符), 且只支持那些不需要更多处理的语言象 拉丁, 希腊, 斯拉夫 和许多科学用符号. 在这个级别上, UCS 支持与 ISO 8859 支持类似, 唯一显著的区别是现在我们有几千种字符可以用了, 其中的字符可以用多字节串来表示.

总有一天 Linux 会当然地支持组合字符, 但即便如此, 对于组合字符串, 预作字符(如何可用的话)仍将是首选的. 更正式地, 在 Linux 下用 Unicode 对文本编码的首选的方法应该是定义在 Unicode Technical Report #15 里的 Normalization Form C.

在今后的一个阶段, 人们可以考虑增加在日文和中文里用到的双字节字符的支持 (他们相对比较简单), 组合字符支持, 甚至也许对从右至左书写的语言如希伯来文 (他们可不是那么简单的) 的支持. 但对这些高级功能的支持不应该阻碍简单的平板 UTF-8 在 拉丁, 希腊, 斯拉夫和科学用符号方面的快速应用, 以取代大量的欧洲 8 位编码, 并提供一个象样的科学用符号集.

我该怎样修改我的软件?

有两种途径可以支持 UTF-8, 我称之为软转换与硬转换. 软转换时, 各处的数据均保存为 UTF-8 形式, 因而需要修改的软件很少. 在硬转换时, 程序将读入的 UTF-8 数据转换成宽字符数组, 以在应用程序内部处理. 在输出时, 再把字符串转换回 UTF-8 形式.

大多数应用程序只用软转换就可以工作得很好了. 这使得将 UTF-8 引入 Unix 成为切实可行的. 例如, 象 cat 和 echo 这样的程序根本不需要修改. 他们仍然可以对输入输出的是 ISO 8859-2 还是 UTF-8 一无所知, 因为它们只是搬运字节流而没有处理它们. 它们只能识别 ASCII 字符和象 ’’ 这样的控制码, 而这在 UTF-8 下也没有任何改变. 因此, 这些应用程序的 UTF-8 编码与解码将完全在终端模拟器里完成.

而那些通过数字节数来获知字符数量的程序则需要一些小修改. 在 UTF-8 模式下, 它们必须不数入 0×80 到 0xBF 范围内的字节, 因为这些只是跟随字节, 它们本身并不是字符. 例如, ls 程序就必须要修改, 因为它通过数文件名中字符数来排放给用户的目录表格布局. 类似地, 所有的假定其输出为定宽字体, 并因此而格式化它们的程序, 必须学会怎样数 UTF-8 文本中的字符数. 编辑器的功能, 如删除单个字符, 必须要作轻微的修改, 以删除可能属于该字符的所有字节. 受影响有编辑器 (vi,emacs, 等等)以及使用 ncurses 库的程序.

Linux 核心使用软转换也可以工作得很好, 只需要非常微小的修改以支持 UTF-8. 大多数处理字符串的核心功能 (例如: 文件名, 环境变量, 等等) 都不受影响. 下列地方也许必须修改:  

控制台显示与键盘驱动程序 (另一个 VT100 模拟器) 必须能编码和解码 UTF-8, 必须要起码支持 Unicode 字符集的几个子集. 从 Linux 1.2 起这些功能已经有了.  

外部文件系统驱动程序, 例如 VFAT 和 WinNT 必须转换文件名字符编码. UTF-8 已经加入可用的转换选项的列表里了, 因此 mount 命令必须告诉核心驱动程序用户进程希望看到 UTF-8 文件名. 既然 VFAT 和 WinNT 无论如何至少已经用了 Unicode了, 那么 UTF-8 在这里就可以发挥其优势, 以保证转换中无信息损失.

POSIX 系统的 tty 驱动程序支持一种 “cooked” 模式, 有一些原始的行编辑功能. 为了让字符删除功能工作正常, stty 必须在 tty 驱动程序里设置 UTF-8 模式, 因此它就不会把 0×80 到 0xBF 范围内的跟随字符也数进去了. Bruno Haible 那里已经有了一些 stty 和核心 tty 驱动 程序的 Linux 补丁 了.  

C 对 Unicode 和 UTF-8 的支持

从 GNU glibc 2.1 开始, wchar_t 类型已经正式定为只存放独立于当前 locale 的, 32位的 ISO 10646 值. glibc 2.2 开始将完全支持 ISO C 中的多字节转换函数 (wprintf(),mbstowcs(),等等), 这些函数可以用于在 wchar_t 和包括 UTF-8 在内的任何依赖于 locale 的多字节编码间进行转换.

例如, 你可以写

wprintf(L”Sch鰊e Gr e!”);

然后, 你的软件将按照你的用户在环境变量 LC_CTYPE (例如, en_US.UTF-8 或 de_DE.ISO_8859-1) 中选择的 locale 所指定的编码来打印这段文字. 你的编译器必须运行在与该 C 源文件所用编码相应的 locale 中, 在目标文件中以上的宽字符串将改为 wchar_t 字符串存储. 在输出时, 运行时库将把 wchar_t 字符串转换回与程序执行时的 locale 相应的编码.

注意, 类似这样的操作:

char c = L”a”;  

只允许从 U+0000 到 U+007F (7 位 ASCII) 范围里的字符. 对于非 ASCII 字符, 不能直接从 wchar_t 到 char 转换.

现在, 象 readline() 这样的函数在 UTF-8 locale 下也能工作了.

怎样激活 UTF-8 模式?

如果你的应用程序既支持 8 位字符集 (ISO 8859-*,KOI-8,等等), 也支持 UTF-8, 那么它必须通过某种方法以得知是否应使用 UTF-8 模式. 幸运的是, 在未来的几年里, 人们将只使用 UTF-8, 因此你可以将它作为默认, 但即使如此, 你还是得既支持传统 8 位字符集, 也支持 UTF-8.

当前的应用程序使用许许多多的不同的命令行开关来激活它们各自的 UTF-8 模式, 例如:   xterm 命令行选项 “-u8″ 和 X resource “XTerm*utf8:1″   gnat/gcc 命令行选项 “-gnatW8″   stty 命令行选项 “iutf8″   mined 命令行选项 “-U”   xemacs elisp 包裹 以在 UTF-8 和内部使用的 MULE 编码间转换   vim ’fileencoding’ 选项   less 环境变量 LESSCHARSET=utf-8   记住每一个应用程序的命令行选项或其他配置方法是非常单调乏味的, 因此急需某种标准方法. 如果你在你的应用程序里使用硬转换, 并使用某种特定的 C 库函数来处理外部字符编码和内部使用的 wchar_t 编码的转换工作, 那么 C 库会帮你处理模式切换的问题. 你只需将环境变量 LC_CTYPE 设为正确的 locale, 例如, 如果你使用 UTF-8, 那就是en.UTF-8, 而如果是 Latin-1, 并需要英语的转换, 则设为 en.ISO_8859-1. 然而, 大多数现存软件的维护者选择用软转换来代替, 而不使用 libc 的宽字符函数, 不仅因为它们还未得到广泛应用, 还因为这会使得软件进行大规模修改. 在这种情况下, 你的应用程序必须自己来获知何时使用 UTF-8 模式. 一种方式是做以下工作: 按照环境变量 LC_ALL, LC_CTYPE, LANG 的顺序, 寻找第一个有值的变量. 如果该值包含 UTF-8 子串 (也许是小写或没有”-”) 则默认为 UTF-8 模式 (仍然可以用命令行开关来重设), 因为这个值可靠又恰当地指示了 C 库应该使用一种 UTF-8 locale. 提供一个命令行选项 (或者如果是 X 客户程序则用 X resource 的值) 将仍然是有用的, 可以用来重设由 LC_CTYPE 等环境变量指定的默认值. 我怎样才能得到 UTF-8 版本的 xterm? 在 XFree86 里带的 xterm 版本最近已经由 Thomas E. Dickey 加入了支持 UTF-8 的扩展. 使用方法是, 获取 xterm patch #119 (1999-10-16) 或更新版本, 用 “./configure –enable-wide-chars ; make” 来编译, 然后用命令行选项 -u8 来调用 xterm, 使它将输入输出转换为 UTF-8. 在 UTF-8 模式里使用一个 *-ISO10646-1 字体. 当你在 ISO 8859-1 模式里时也可以使用 *-ISO10646-1 字体, 因为 ISO 10646-1 字体与 ISO 8859-1 字体是完全向后兼容的

没有评论 »

转稿

八月 9, 2008 | c/c++ | RSS 2.0

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/flickr_architecture_part_i/

Flickr(http://www.flickr.com/)是国外一个领先的图片分享网站,现在应该在yahoo门下,感觉yahoo还是有很多好东西,奈何资本要抛弃他了。这个轮回其实挺有意思的,起先是做实业被microsoft郁闷了,说软件是虚的值不能那么多钱,然后microsoft被yahoo郁闷了,说互联网是虚的不值那么多钱,然后是yahoo被google郁闷了,yahoo比较厚道没说什么,现在microsoft要收购yahoo了(折腾好久了,估计要落听了吧),不知道google将来要被谁郁闷了。成功建立在相同的失败上,反过来失败都是建立在相同的成功上也成立,进入正题吧。

原文地址是http://highscalability.com/flickr-architecture,本文不是原文的严谨翻译,带有我的理解以及补充,由于水平有限,文中的错误请各位斧正。

Flickr处理的数据:

  • 多达40亿次的请求(http request or database query?不知道了,不管是哪个,都够大的吧。)
  • squid总计约有3500万张图片(硬盘+内存)
  • squid内存中约有200万张图片
  • 总计有大约4亿7000万张图片,每张图片大概4~5MB
  • 每秒3,8000次请求 (存储了1200万对象在里面)
  • 2 PB 存储(星期天要消费~1.5TB)
  • 每天新增图片超过 400,000

吓死人不偿命….

Flickr用到的技术:

  • PHP
  • MySQL
  • Shards
  • Memcached 作为中间缓存层,memcached在web2.0网站中可能是引用最广泛的产品之一,开源&强大.
  • Squid 作反向代理服务器(reverse-proxy for html and images).
  • Linux (RedHat),如果你想用RedHat企业版又不想付费,试试这个CentOS,基本上100%克隆RedHat企业版(估计传说中1%的RedHat代码没有),我用的就是这个。
  • Smarty 作为模板解析,很多人在讨论smarty这不好那不好,但是大网站都在用,稳定而且功能强大,系统的瓶颈从来不会再smarty这里,我保证。
  • Perl 估计用perl做一些系统层面的东西吧,比如日志处理(猜测)
  • PEAR 做XML和Email解析,和我们一样,Flickr用的也是PHP4,不过新项目还是用PHP5吧
  • ImageMagick 图像处理的不二选择
  • Java, for the node service,Java就不太了解了,希望读者补充
  • Apache 大家都在用,尝鲜的用户nginx或者lighttpd(适合静态文件,youtube用它做媒体文件服务器),出了问题你会抓狂的。
  • SystemImager 作为服务器部署
  • Ganglia 分布式系统监控,或者你可以试试nagios,据我所知也很多公司在用
  • Subcon 用SVN维护服务器配置文件并且可以部署不同的配置文件到服务器集群中去(这个我没用过,系统运维的可能会喜欢)
  • Cvsup 文件分发,是否类似rsync?
  • Wackamole前端负载均衡,类似的产品有http://haproxy.1wt.eu/

Flickr架构

常见的Squid反向代理、PHP App Servers、Net App’s、Storage Manager我在这里就不讲,我们关注一些让人兴奋的特征:

  • Mysql的Master-Master结构,mysql的常见的master-slave结构,大家都知道存在”single pointoffailure”(单点故障的问题),且只对读操作有好处,对于写频繁的网站却不是一个好的解决方案,Flickr的双master方案据我推测用的就是这个http://code.google.com/p/mysql-master-master/,原理就是master轮询,保证同时只有一个master负责写,解决了单点故障的问题。
  •  Dual Tree Structure,看看下面的图就知道什么是“双树结构”(姑且这么翻译)

    示例图中上方的2台机器为master,下方的4台为slave,这种“双树结构”的设计保证一个slave只有一台master,易于扩展也不会形成环路。原文中说这种设计是1+1=200%的设计,简单高效。为了防止自增长冲突,数据表中无自增长列。
    补充:对于大型应用的分表设计,防止自增长冲突是个问题,有个简单的方案:比如分3张表,可以设第一张表从1开始以3跳跃递增,那么第一张表存储的序列为1,4,7,10……,第二张表从2开始也以3跳跃递增,第二张表存储的序列为2,5,8,11……,第三张表从3开始以3跳跃递增,第三张表存储的序列为3,6,9,12……,保证不会有重复的序号,但这种方案的缺点是如果数据爆炸,3张表不够,你分4张表呢?需要手工迁移数据,如果程序写的不好,底层又要大动了。
    Flickr采用的方案是一个中心’users’table(用户表),记录的信息是用户主键以及此用户对以的数据库片区(有点类似Key->Value的设计,这样的数据结构查询起来是非常迅速的,据说Google的用户登录数据用的就是这样的设计,通过改进版的BDB数据库存储用户名和密码,这样登录起来就不用去查那个大表了),从中心用户表中查出用户数据所在位置,然后直接从目标位置中取出数据。
  • 用专门的服务器存储静态内容,这个容易做到,比如专门的图片服务器。
  • “Use a share nothing architecture”这个比较费解了,字面上的意思是使用一个无共享的架构,其实就是解除架构上的依赖,类似我们写程序解耦合一样。
  • 除图片外,所有的数据都存在数据库中,这里他们提到了PHPsession,我们知道php的session是存储在服务器文件系统的,而且默认没有做hash目录,这就意味着如果你的网站访问量大,比如有10万个人在线,你的session目录下就有10万个文件,如果你的文件格式是NTFS(windows)或者Ext3(Linux),你要定位到某个文件,系统基本上会假死,有个好的建议是不要在一个文件夹下放超过1000个文件。使用默认的phpsession还有另外一个问题:服务器session同步,用户在A服务器登录后,session存储在A服务器上,然后应用跳转到B服务器,B服务器上的session没有同步就出问题了,当然解决这个问题的方法很多,比如统一存储session,所有服务器的session都存储在一个vfs设备上,或者通过cookie重新生成一个session在B服务器上

写一篇这样的文章非常的辛苦,第一部分就先写到这里,待续…

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/flickr_architecture_part_ii/

Flickr的架构不能说是完美的,没有完美的架构,ebay对于扩展有以下建议:

  • 不要预先去为性能扩展,出现问题之后找到问题再寻扩展;
  • 不要想寻找到一个一劳永逸的方案,因为你不知道下一个瓶颈在哪里;
  • 访问量大了,出了问题,修改架构,稳定运行,访问量再大了,又出问题了,再修改,这个是解决问题的唯一方案。

Flickr是Lamp架构比较成功的案例之一,抛出Flickr的架构是因为看到国内很多的架构设计盲目、迷信以及短视,不过相对于架构来说,程序的结构更让人担忧,后面的我会写一些关于程序结构的文章,希望能和大家一起讨论成长,好了,我们继续Flickr的架构。

  • “Statelessness”设计,原文用的是这个词,字面上的意思是“无国家的”,看了一些相关文档,我觉得Statelessness的含义是“无界限的”设计,一个简单的例子,现在很多架构设计用到分表,比如用户信息表,怎么分呢?直接hash分表,两张表就按奇偶分,n张表就按n的模进行分,这种设计就是Statelessness的反向,你把你的用户绑定在一张固定的表或者固定的机器上了,如果你的用户里面有付费用户,你希望把他们的数据单独存储或者用专门的机器处理,你怎么办?你设计的太死了,你的付费用户只能和免费用户绑定在一起,提供一样的服务器支持,当然,你可以骗用户说他们的服务是有差别的。
  • 通过master-save的设计能解决一部分问题,但很快你就会发现不行了,常见的master-slave只能解决读的问题,但存在单点失败故障,而且当负载比较重的时候会存在复制延迟的问题,很多公司都会碰到。
  • 搜索功能由专门的服务器群来支持,通过复制需要搜索的内容到搜索服务器去搜索,和App servers分开。
  • 集群
    1、分表:按照一定主键拆分数据表,比如按照用户划分;
    2、一个用户的所有信息在同一组服务器上
    3、数据能够在不同的服务器组上迁移(Statelessness)
    4、一组中心服务器负责查询,比如定位某个用户在哪个服务器组
    5、不要以用户ID作为分组的依据(Non-Statelessness)
  • 服务器组中每台服务保持50%的负载,当某台服务器down机或者维护时,另外一台服务器100%负载
  • 访问高峰时,每台服务器的负载可能高于50%,现在Flickr通过增加服务器来让服务器负载在50%以下
  • 每个页面大概有27~35个mysql query(够高的),点击数统计、API调用数据库都是实时的(太恐怖了,估计只有Dathan Pattishall会这么变态的使用mysql)
  • 每组服务器处理40万+的用户数据,很多数据存储了双份,比如A用户对B用户的博客进行了评论,那么评论的数据在A的数据表里面和B的数据表里面都存储了一份,Flickr通过事务来保证同步。(这样做的目的是保证同一个用户的数据都存放在同一组服务器上,省去复制的成本。)
  • Flickr的硬件设置:Intel EMT64处理器/红帽RHEL4/16GB内存/6块15000转的硬盘做RAID-10/12TB用户数据(仅仅是数据库而不是图片)/2U服务器,每个服务大概有120GB数据
  • 备份:每天不同时刻跑cron,每天晚上做数据库快照,专门的服务组来备份(和线上业务分开),交叉备份(比如每天、每周、每月)
  • 每张图片都有自己的档案,档案包括大小、尺寸、像素等等,储存在数据中
  • 每组服务器最多400个连接,45个线程缓存
  • Tag标签,Flickr发现常见的数据库结构不能很好的处理巨大的标签库,他们采用的是类似倒排索引以及大量的缓存来处理,并且Tag标签不是实时的,他们会在线下进行统计
  • Flickr目标是所有的事务都做成实时的,没有延迟(个人觉得倒是没有这个必要)

Todd Hoff总结的经验:

  • 不要把你的应用简单的看成一个Web应用,可能会有REST APIs, SOAP APIs, RSS feeds, Atom feeds等等的应用
  • “无界限”设计,不要把你的用户死死的绑定在某个服务器上
  •  产品设计时需要做扩容的计划以及预算
  • 慢慢来,不要一开始就买一堆服务器
  • 实地考察,不要臆想,获得实际数据之后再做决定
  • 内建日志系统,记录服务器和应用日志
  • Cache,缓存是必不可少的
  • 抽象层,由于你的架构随时可能变,架构的变化必定要带来底层的变化,这就需要你在底层的基础上根据业务封装一层中间层,这样底层的改动不至于影响业务(这个太重要了,不要因为扩展把原来的程序推倒重来)
  • 迭代开发,随时改进
  • 忘记那些调优的小技巧吧,比如很多人对与PHP里面的require和require_once的性能差别,这些性能的差异和架构上的短板比起来根本不足为道
  • 在线上测试你的效果
  • 忘记用工具测试出来的结果,这些结果只能给你一个大概的印象而已
  • 找出你的系统短板,一台服务器的最大处理能力是多少?现在离最大负载还有多远?mysql的瓶颈在哪里?是不是磁盘IO?memcache的瓶颈在哪里?CPU还是网络传输?
  • 注意你的用户使用规律,比如Flickr发现每年的第一个工作日比平时多20%~40%的上传量,周日的访问量比平时要多40%~50%
  • 要注意指数型的增长
  • 你的计划是为你访问的峰值设计的

补充:阅读完原文的评论,有一个评论翻译出来给大家分享:

Flickr如何存储图片的呢?

标准的Flickr图片Url是这样的http://farm1.static.flickr.com/104/301293250_dc284905d0_m.jpg,其中farm1是Flickr的服务器群,static.flickr.com是Flickr静态图片服务器,104是服务器ID,301293250是图片ID,dc284905d0是Flickr的加密串,防止盗链,m表示图片的尺寸。m表示中等尺寸

后记:

终于“翻译”(姑且用这个词)完了,看到原文的一个评论是”Hmm… i can not beleive flickr written onphp…”,借用好像也是Flickr的人说的一句话:扩展的不是语言,而是架构。国内很多大的企业都在用PHP(比如我所在的sina),PHP总给人是草根语言的感觉,是因为没有人肯分享自己的架构,以及程序员写程序的时候不注意自己的结构(设计模式),好的架构只能让你的程序跑的更快,好的结构让你的程序更易于维护,更容易让别人看的懂,更容易团队合作。

Tags: ,

没有评论 »

三年的暑假

八月 8, 2008 | 数学 | RSS 2.0

又是一度七夕节,酷热的暑假迎来了举世瞩目的2008奥运会,刚刚走完了大学的历程,许许多多的事情还未来的及封存,值得回顾的三个大学暑假。

大二的暑假,是那样的苦难的,那样的艰辛 ,可以说历尽九九八十一难,刻骨铭心的还是那20多天,让我尝尽了无数同龄人没有经历过的生活,可以想象,三四十度的高温下的体力活,前十天是修桥,好像记得是什么国家的水利工程,有个比我大一点的村里的,每天天蒙蒙亮的时候,我起来,就是开始被当奴隶使唤,挖土,还有些如把淤泥从河底里拉出来,然而夏天的雨经常光顾,所以是拉好了,又得继续拉,有时候是顶着将近四十度的高温拉土,也许是这段经历,致使我黝黑的脸上还能依稀的得到证明,每天也不知道工作几个小时,天没亮开工,天黑了一会了吃饭。最痛苦的是晚上睡觉的地方连个屋子也没有,有个凉席,然后下面就是转头,带了蚊帐也不当用,文字是超级多,每天晚上不是去睡觉,而是去和那些蚊子去抗争,无数次把我的香吻献给了他们,刚去没几天好像我的腿被露水露了,正好下雨,不能干活,最后疼的回家了,过了几天又去哪里接受了非人的蹂躏,总共干了12天左右吧,每天20,吃的比喂猪好一点。挣了200多一点老老实实回家了。在家没待多长时间,那个时候也许是不安分守己,也许是甘于接受蹂躏,我又去郑州跟着几个堂兄弟干建筑,在家也是闲着,而且工资好像每天是25,这次比上次更接近奴隶生活,身体上也接受了考验,在老板的监视下搬转,拉土等。有时候是从二楼拉一楼的泥,清晰的记得我那双用来拥抱女人的手被蹂躏的每个手指上都是口子,记得有时候疼的都拉不上去了,脚上也是每个上面都打了眼,因为是沙子,经常在上面踩,只记得一个感觉,只要有人回家我马上回家,那时候想家想的是泪汪汪的,记得刚到没几天,传砖的时候脚被砸了,而且流血了,堂兄弟陪着我包了以后,回来接着上班,不然一天的工钱就没了。幸好的是10天左右由一个村子里的人家里有事,给了得以脱身这个人间地狱的机会。干了十天,本想可以哪的个250,遗憾的是人家老板说我没说要给你每天25啊,就你那么小,也干不了多少活,给你20吧,那时候我的心哇啦哇啦的凉啊,但我还是和他争了几句,我说我不比别人少干一点,不给我算了,我才不在乎那一天钱了,老板楞了楞给了我每天22吧好像。哈哈。虽然那个暑假有将近2个月,但是这20多天成为了我的全部。他让我知道了生活的苦难,也让我知道了爸爸弟弟妹妹出外打工的不容易。所以以后每天但自己遇到不如意的时候想想这些,我就会看得很开。

 大三的暑假,值得回味的不仅是那些生活的艰辛,还有生活的浪漫,真挚的情感,那一年的暑假我和我结识女朋友南下福建,哪里有一个认识的朋友,带了好像5,6百吧,心想到地方有朋友接我怕什么,捡了个便宜,我们俩在学校买了个到厦门的半价票,本打算的是先到厦门然后再去泉州,到武汉转车,从南阳到武昌,到了武昌火车站用报纸当席,用天空当被,我们度过了一个难熬的一个晚,然后坐上了南下的火车,一座就是30多个小时,在朋友哪里住了几个晚上,然后自己租了个房子,待了几天朋友给找了个工作,不管吃不管住,700多,去了就是擦鞋啊,画鞋印啊,也是每天很早上班,晚上12点下班,挺苦,挺累,就那样我和我女朋友坚持了下来,那时候重要的是没钱吃饭,也不敢和家里人要,每天早上花上5毛钱,吃个包子,上午花点钱吃馒头或者米饭,很感谢福建有些地方的米是随便可以吃的,如果不是这样,不知道那时候我会不会真的成为了排骨,后来女朋友工作的花鞋样没有工作了,然后我把我的让给了她,自己准备再去找一份,再那个地方,只要有傻力气,不太傻,就能找到工作,酷热的福建,我跑了好多厂,最后敲定了两家,选择了已经离我们住的很近的背包厂,至今还记得叫福建泉州毅达包袋有限公司,以前的那个厂叫南丰鞋厂,然后去哪里每天的工作是穿背带一个2毛,去扣一个2分等等的杂货,每天也是工作时间长达12个小时,有时候女朋友下班了去帮帮我,还好坚持了下来。基本上没有一天休息的,那时候的想法是能挣钱就干,差点去了干搬运工,因为搬运工每天是50,而且是每天发工资,虽然担心自己撑不下来,但是愿意试试,很庆幸没有找到这样的机会,没有休息,没有假期,每天的坚苦卓绝的劳作,有件事情比较深刻,就是有一次公司的货物要出厂,搬箱子上集装箱,心想挣大钱的机会到了,因为装货物,好时运点一天能挣40多吧,干开始有个和我年龄大小差不多的,还有几个形式上的大人,称他们为形式上的大人,因为他们竟然竟然不但不帮我,而且坑害我,说腰伤住了,我拉驾驶车,你搬,哎,可苦了我了,福建的40多度的天吧,一个大箱子的,一个大箱子的往上搬啊,累的要命啊,拿了不到45的钱,同时也创了我的吉尼斯纪录,我们总共在福建待了40多天吧,我干了28天的工,挣了将近900元的钱,女朋友虽然干了快四十天,挣了800多一天。拿到钱的那一刻,心情不能用言语了表达了,有种从地狱冲到天堂般的感觉,然后是逛泉州那块的公园啊,景,同时去了一个泉州的一个大桥,记得一个画面,我和女朋友躺在不知道是该叫海,还是叫河的水边,任海风轻轻吹拂,那时候感觉真幸福,感谢女朋友对我的理解,艰难中我们一路携手走来,还得也是一个七夕情人节,那天晚上本来就没有多少钱的我破例给女朋友买了朵玫瑰花,花了10元钱,虽然那多花已经凋零,但他永远开在我的心里,坚信风雨过后必定是彩虹。记得有很多温馨的时刻。

大三的暑假还挺好,即挣钱了,又学到了知识了,帮老师做了两个网站挣了七八百吧。

现在已经工作了,但忘不了那些日子,忘不了经历过的生活的艰辛,所以我很懂得珍惜现在的生活,懂得知足,懂得生活的不易。懂得去为了生活去努力,好好的生活。谨以此文做记。顺附我喜欢的两首诗。

相信未来

当蜘蛛网无情地查封了我的炉台

当灰烬的余烟叹息着贫困的悲哀

我依然固执地铺平失望的灰烬

用美丽的雪花写下:相信未来

当我的紫葡萄化为深秋的露水

当我的鲜花依偎在别人的情怀

我依然固执地用凝霜的枯藤

在凄凉的大地上写下:相信未来

我要用手指那涌向天边的排浪

我要用手掌那托住太阳的大海

摇曳着曙光那枝温暖漂亮的笔杆

用孩子的笔体写下:相信未来

我之所以坚定地相信未来

是我相信未来人们的眼睛

她有拨开历史风尘的睫毛

她有看透岁月篇章的瞳孔

不管人们对于我们腐烂的皮肉

那些迷途的惆怅、失败的苦痛

是寄予感动的热泪、深切的同情

还是给以轻蔑的微笑、辛辣的嘲讽

我坚信人们对于我们的脊骨

那无数次的探索、迷途、失败和成功

一定会给予热情、客观、公正的评定

是的,我焦急地等待着他们的评定

朋友,坚定地相信未来吧

相信不屈不挠的努力

相信战胜死亡的年轻

相信未来、热爱生命

……………………………………………………

……………………………………………………

热爱生命

也许我瘦弱的身躯象攀附的葛藤,

把握不住自己命运的前程,

那请在凄风苦雨中听我的声音,

仍在反复地低语:热爱生命。

也许经过人生激烈的搏斗后,

我死得比那湖水还要平静。

那请去墓地寻找的我的碑文,

上面仍刻着:热爱生命。

我下决心:用痛苦来做砝码,

我有信心:以人生去做天秤。

我要称出一个人生命的价值,

要后代以我为榜样:热爱生命。

的确,我十分珍爱属于我的

那条曲曲弯弯的荒槽野径,

正是通过这条曲折的小路,

我才认识到如此艰辛的人生。

我流浪儿般的赤着双脚走来,

深感到途程上顽石棱角的坚硬,

再加上那一丛丛拦路的荆棘

使我每一步都留下一道血痕。

我乞丐似地光着脊背走去,

深知道冬天风雪中的饥饿寒冷,

和夏天毒日头烈火一般的灼热,

这使我百倍地珍惜每一丝温情。

但我有着向旧势力挑战的个性,

虽是历经挫败,我绝不轻从。

我能顽强地活着,活到现在,

就在于:相信未来,热爱生命。

 

 

 

 

 

 

 

 

没有评论 »

hash算法

八月 4, 2008 | mysql | RSS 2.0

http://burtleburtle.net/bob/hash/doobs.html
http://code.sixapart.com/svn/memcached/trunk/server/assoc.c

没有评论 »