在将文本存储到数据库之前进行压缩

17

我需要在mysql数据库中存储大量文本。它将是数百万条记录,字段类型为LONGTEXT,数据库大小将非常巨大。

所以,我想问一下,是否有安全的方法在将文本存储到TEXT字段之前对其进行压缩以节省空间,并能够在需要时提取回来的能力?

类似于:

$archived_text = compress_text($huge_text);
// saving $archived_text to database here
// ...

// ...
// getting compressed text from database
$archived_text = get_text_from_db();
$huge_text = uncompress_text($archived_text);

有没有用php或mysql实现这个的方法?所有的文本都是utf-8编码。

更新:

我的应用程序是一个大型的文学网站,用户可以添加他们的文章。这是我拥有的表格:

CREATE TABLE `book_parts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `book_id` int(11) NOT NULL,
  `title` varchar(200) DEFAULT NULL,
  `content` longtext,
  `order_num` int(11) DEFAULT NULL,
  `views` int(10) unsigned DEFAULT '0',
  `add_date` datetime DEFAULT NULL,
  `is_public` tinyint(3) unsigned NOT NULL DEFAULT '1',
  `published_as_draft` tinyint(3) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `key_order_num` (`order_num`),
  KEY `add_date` (`add_date`),
  KEY `key_book_id` (`book_id`,`is_public`,`order_num`),
  CONSTRAINT FOREIGN KEY (`book_id`) REFERENCES `books` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

目前有大约800k条记录,占用4 GB的空间,99%的查询是SELECT。 我有充分的理由认为这些数字将呈几何级增长。 因为周围存在相当重的逻辑,并且我的网站有相当多的点击量,所以我不想在文件中存储文本。


如果您要存储二进制数据,请使用BLOB字段,而不是文本。 - Brad
1
您可能需要重新考虑对基于文件的存储的否决。我不确定您所说的“重逻辑”是什么意思,但是我不认为将文本存储在数据库中对于访问量很大的网站而言自动比文件更好。 - grossvogel
6个回答

19

您是否打算索引这些文本?读取和插入的负载量有多大?

您可以使用InnoDB数据压缩 - 这是一种透明且现代化的方式。有关更多信息,请参见文档

如果您有非常巨大的文本文件(例如,每个文本文件大于10MB),那么最好不要将它们存储在MySQL中。在文件系统中存储由gzip压缩的文本,并仅在MySQL中存储指针和元数据。您可以轻松扩展存储空间,并将其移到例如DFS中。

更新:在MySQL之外存储文本的另一个好处:数据库保持小巧快速。缺点:数据不一致的概率很高。

更新2:如果您有足够的编程资源,请查看此类项目: http://code.google.com/p/mysql-filesystem-engine/.

最终更新:根据您的信息,您可以使用InnoDB压缩 - 它与ZIP相同。您可以从这些参数开始:

CREATE TABLE book_parts
 (...) 
 ENGINE=InnoDB
 ROW_FORMAT=COMPRESSED 
 KEY_BLOCK_SIZE=8;

接下来你需要玩弄 KEY_BLOCK_SIZE。请查看 SHOW STATUS LIKE 'COMPRESS_OPS_OK'SHOW STATUS LIKE 'COMPRESS_OPS'。这两个参数的比率必须接近于1.0:文档


如果您正在使用InnoDB,那就是正确的选择。然而,如果使用不支持压缩的引擎...此外,我看到很多理由不要在数据库中“仅存储文件指针”(也有很多理由这样做- TIMTOWTDI,这真的取决于您的需求)。 - Romain
@Oroboros102,请看一下更新。我理解得对吗,InnoDB压缩只压缩索引?那这不是我的情况... - Silver Light
不,压缩用于数据和所有索引(主键、次要、复合)的存储。 - Oroboros102
谢谢您的回答。我会选择这种方式,因为这样可以最小化对我的应用程序的更改。 - Silver Light
请不要忘记,压缩需要满足许多要求。从mysql版本到特殊的表存储方式开始。您需要“每个表一个文件”,我记得。所有这些都可以在文档中找到:http://dev.mysql.com/doc/innodb-plugin/1.0/en/innodb-compression-enabling.html - Oroboros102

10
如果您正在压缩(例如gzip),请勿使用任何类型的TEXT字段。它们不是二进制安全的。输入/输出文本字段的数据将受到字符集转换的影响,这可能会(但并非必然)破坏压缩的数据,并在检索/解压文本时给您带来损坏的结果。
相反,请使用BLOB字段,它们是二进制透明的,不对数据进行任何转换。

谢谢关于数据类型的信息。但是压缩方面怎么办? - Silver Light
压缩会导致无法搜索文本,因为您必须解压才能再次获取原始文本。如果您永远不打算在数据库中查找文本,请不要首先将压缩(或原始)文本存储在数据库中。将其外部存储在文件中,并在数据库中存储一些引用(文件名/路径)即可。 - Marc B
gzcompress在这种方法中表现非常好,但请考虑:http://www.mysqlperformanceblog.com/2008/01/11/mysql-blob-compression-performance-benefits/。 - Narcis Radu

6

把文本字段定义为blob可能更好,然后在PHP中压缩数据以节省通讯成本。

CREATE TABLE book_parts (
    ......
    content blob default NULL,
    ......
)

在PHP中,可以使用gzcompress和gzuncompress函数。

$content = '......';
$query = sprintf("replace into book_parts(content) values('%s') ",
        mysql_escape_string(gzcompress($content)) );
mysql_query($query); 


$query = "select * from book_parts where id = 111 ";
$result = mysql_query($query);
if ($result && $row = mysql_fetch_assoc($result))
    $content = gzuncompress($row['content']);

2

1
你可以使用PHP函数gzdeflate和gzinflate来处理文本。

0

将大文本压缩到数据库中没有任何好处。

以下是您可能会在长期内面临的问题:

  • 如果服务器崩溃,数据可能很难恢复。
  • 不适合搜索。
  • 在mysql服务器和浏览器之间传输数据需要额外的时间。
  • 备份耗时(不使用复制)。

我认为将这些大文本存储到磁盘文件中会更容易实现以下目标:

  • 分布式备份(rsync)。
  • PHP处理文件上传。

2
我不同意。 在任何正常的服务器上,解压缩gzip流所需的时间与在线延迟相比是无关紧要的,您可以完全忽略它。 您不需要搜索每个文本字段,通常只需要访问它即可。 - John

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接