首页 > 代码库 > MyISAM单一表突破4G限制的实现方法
MyISAM单一表突破4G限制的实现方法
MyISAM单一表突破4G限制的实现方法
MyISAM单一表突破4G限制的实现方法
(2007-05-05 18:18:25)标签: mysqlmyisam表4g限制 | 分类: LAMP |
近日,一位Discuz!老用户的论坛在发表回复时出现“The table is full”的提示,字面意义上是数据表已满的意思。因为很少有开发者遭遇单一表超过4G的情况,因此朋友间的讨论只能提供一些外围的信息。为解决此问题,我翻阅了很多资料,本文将以我此次问题的解决过程,介绍问题发生的原因及对策。根据经验,The table is full提示往往出现在以下两种情况:
1. 表中设置了MAX_ROWS值,简单的说,若MAX_ROWS设置为100,而程序试图写入第101条记录,会出现此错误。 2. 表满。这种情况是本文讨论的重点。 |
我们认为MySQL在存取表的时候,存在一种定位分配规律。这个规律在默认的情况下,可以寻址4G以内的数据。超过这个大小,数据库将不能对数据定位,因而也无法进行读写。经过实验,这个限制是完全可以被突破的。本例中,用户的系统环境为双Athlon处理器、SCSI硬盘72G、2G内存,用户的帖子表数据尺寸为4294963640,接近4G(4G的实际字节数为4294967296)。
首先SSH登录后,查看用户的系统信息:
# uname -a Linux zichen.com 2.4.20-8smp #1 SMP Thu Mar 13 16:43:01 EST 2003 i686 athlon i386 GNU/Linux |
证明是Linux系统,根据内核版本2.4.20-8smp,加上国内使用的常见系统,估计应该是redhat 9发行包。
# cat /etc/*release* Red Hat Linux release 9 (Shrike) |
这也证明了我们对系统版本的猜想。
然后看一下用的是什么文件系统。因为该用户并非高手,估计在装系统的时候就是一路回车下来,redhat 9默认的应该是EXT3,不过我们还是看一下:
# parted GNU Parted 1.6.3 Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This program is free software, covered by the GNU General Public License. |
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
Using /dev/sda Information: The operating system thinks the geometry on /dev/sda is 8942/255/63. Therefore, cylinder 1024 ends at 8032.499M. (parted) print Disk geometry for /dev/sda: 0.000-70149.507 megabytes Disk label type: msdos Minor Start End Type Filesystem Flags 1 0.031 101.975 primary ext3 boot 2 101.975 10103.378 primary linux-swap |
证明确实是这样子。随后我们翻阅了EXT3文件系统的相关技术参数,EXT3是在EXT2基础上演变而来。EXT2所支持最大单一文件长度是2G,这个是很蹩脚的一个限制。EXT3做的很大一个改善就是将这个限制放大到了2TB,由此稍松一口气,起码不是操作系统上的限制。
经过朋友的开导,了解到单一文件大小有如下几个因素:
1. 文件系统的限制(如刚存所说EXT3的2TB限制) 2. 某一程序进程所能存取的第一文件最大尺寸(例如apache在Linux EXT3下能存取的最大尺寸为2G,诸如日志) |
初步判断瓶颈就在上述其中第二项。随后找到myisamchk来显示一下表信息,证明了瓶颈就在MySQL本身的存取上。
# myisamchk -dv cdb_posts |
结果就不贴了,其中有一项Max datafile length的值恰好就是4G。由此产生了瓶颈。后来翻阅了N多资料,进行了N多尝试,也走了不少弯路,最终觉得还是官方文档比较可靠。比较老的文档里写道这是由于tmp_table_size的值造成的,也有提到用BIG-TABLES这个参数。事实证明这些都是歧途。大晚上的确实很累,这里只给出最终的解决方案吧,中间的就不罗嗦了。
进到mysql客户端。
# mysql -uroot -p Enter password: ****** Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 59411 to server version: 4.0.18-standard |
Type ‘help;‘ or ‘\h‘ for help. Type ‘\c‘ to clear the buffer. |
mysql> use ****** Database changed mysql> ALTER TABLE cdb_posts MAX_ROWS=1000000000 AVG_ROW_LENGTH=15000; |
因为这个表非常大,执行时间在双Athlon的专业服务器上竟然花了30分钟!之后再通过myisamchk查看该表的信息:
# myisamchk -dv cdb_posts MyISAM file: cdb_posts Record format: Packed Character set: latin1 (8) File-version: 1 Creation time: 2004-08-30 22:19:48 Recover time: 2004-08-30 22:42:47 Status: open,changed Auto increment key: 1 Last value: 1063143 Data records: 619904 Deleted blocks: 5 Datafile parts: 619909 Deleted data: 323872 Datafile pointer (bytes): 6 Keyfile pointer (bytes): 4 Datafile length: 4295287332 Keyfile length: 40421376 Max datafile length: 281474976710654 Max keyfile length: 4398046510079 Recordlength: 149 |
table description: Key Start Len Index Type Rec/key Root Blocksize 1 1 4 unique unsigned long 1 4535296 1024 2 5 2 multip. unsigned short 13776 12540928 1024 3 111 4 multip. unsigned long 1 18854912 1024 4 28 3 multip. uint24 18 24546304 1024 5 7 3 multip. uint24 7 32827392 1024 111 4 unsigned long 1 6 7 3 multip. uint24 7 40418304 1024 28 3 uint24 |
令人振奋的事情发生了,该表的 Max datafile length: 281474976710654 Max keyfile length: 4398046510079,即最大数据尺寸(MYD文件)达到了2TB,最大索引尺寸(MYI)仍然为4G。由此默认的4G限制被突破了。关于其中的原理,其实很简单:假设你有一个日记本,上面有10页纸可以写东西,编排目录只需要1个字节(因为0~9就够了)。如果你把这本子又塞进两张纸,变成12页,1个字节的目录空间就无法寻址到后面的两页中,进而产生了错误。上面那个ALTER语句中的数值都是我为保证成功,取的比较大的值(因为ALTER一次实在是太慢了,没时间在那乱试验),相当于告诉数据库,这个本子有1000000000页,每页平均有15000个字节。这样数据库便知道这是很大的一个本子,因此不遗余力的拿出了100页(假设说)做目录编排,这样这个新的目录就可以寻址到日记本的所有内容了。错误消失。
惟一的缺点就是,目录占用的空间多了一些,但已经微乎其微了,做了这种改变其实4G的文件尺寸大小只增大了1M多,非常令人振奋。