MySQL BLOB 图片数据逐渐丢失?

5
在一个 MySQL 的 MyISAM 表中,我有一个列类型为 mediumblob,用于存储捕获的图像作为二进制大对象数据。我遇到了一些有趣且问题重重的图像,其中一些图像正在逐渐失去数据。
Field          type  
--------------------------
image         mediumblob

my.ini中设置了最大允许数据包大小为max_allowed_packet = 8M

image1 image2 image3

这是问题所在

C#应用程序从服务器获取数据时,这种类型的图像每次都会丢失随机大小的数据。在100000+张图像数据中,我得到了10-12张这样的坏图像。

这种行为的原因可能是什么?有没有人有任何想法/解决方案如何修复/避免这个问题。

更新1:
从PictureBox读取字节

MemoryStream ms = new MemoryStream();
byte[] ret = null;

try
{
     picturebox.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
     byte[] Data = new byte[ms.Length];
     ms.Read(Data, 0, (int)ms.Length);
     ret = byteData;
     ms.Close();
 }         
 

将字节数组保存为数据库的中型blob数据。从数据库检索数据时,我将读取器数据强制转换:
byte[] Data = (byte[])reader["Image"];

如果你把图片存储在数据库中,那么你会遇到很多麻烦。为什么不把它们存储为普通文件呢? - Sarke
@niksonkantiPaul,我只是好奇为什么你要将图像保存到数据库中,而不仅仅是将路径和图像保存到文件夹中? - jcho360
5个回答

6
首先,正如Sarke所提到的,将文件内容存储在数据库中并不是最好的选择(文件元数据则是另一回事)。
为什么呢?
1. 性能:在大多数情况下,操作系统文件缓存的表现会优于任何内置于DBMS的功能。 2. 灾难恢复:与文件系统相比,出现故障时丢失所有/大多数文件的几率更高,恢复也更加困难。 3. 扩展性:如果您超出单个服务器的容量,添加应用程序级分片非常容易,并且没有性能损失。多服务器DB设置更加“痛苦”。 4. 有多种解决方案可用/易于迁移:对于大型文件集合存储,有许多硬件和软件解决方案可供选择,而且在它们之间进行迁移比在DBMS之间进行迁移要简单得多。
我存储了近200万张图片,这些图片存储在一个简单的文件夹结构中:/xx/yy/filename,其中filename =文件的md5(+哈希碰撞时的可选编号),xx = md5的前2个字符,yy = md5的第3个和第4个字符。它运行良好,而且应该不会因文件系统相关问题而出现明显的减速(至少有两个数量级)。
回到您的问题,有三种选择:
1. 文件从未正确保存到DB。可能是上传照片的应用程序出了问题或者图像太大。您的max_allowed_packet将图像大小限制为~8 MB,mediub_blob最多可以存储16 MB。为了排除这个问题,请将max_allowed_packet增加到32 MB并进行测试。您需要确保在任何时候都没有超过此大小的图像,并确保应用程序在上传照片时执行得正确。如果您可以找到一个已上传并且在DB中显示良好的图像,但后来无法正常显示,则不是该原因造成的。 2. 文件在更新过程中损坏——如果任何内容以任何方式更新照片,则即使原始文件没问题,更新后的文件也可能出现问题——例如,它可能超过了第1点中的大小限制。 3. (最不可能的一种)如果文件在被存储时没有损坏,那么它是在存储时损坏的——>由于MySQL上没有报告此类错误(而且这不会被忽视),因此我会检查服务器硬件。

只是为了确认:您已确认图像没有问题(至少从数据库中正确获取了一次),并且仅在稍后发现它们现在已经损坏? - c2h5oh
存储为文件?这是个不好的想法,而且性能也不好,看看S3、Azure Blob Store,它们专门设计用于大型文件存储。 - Akash Kava
@AkashKava S3并不使用关系型数据库来存储文件,而是使用键值存储(Amazon Dynamo)。当您的网站/服务发展到需要地理复制等功能时,这种存储解决方案比仅用于文件存储更为优越——它可以实现内容交付、备份、自动优化等功能。 - c2h5oh

4
罪魁祸首是MyISAM存储类型。
我们使用InnoDB存储来存储一百万张图片并进行压力测试,我们得到了适当的结果。要么文件被正确检索,要么根本没有被检索(少于0.01%),因为InnoDB符合ACID标准。
当我们转向MyISAM时,故障率增加到20%,数据丢失也与您的情况相同。原因是MyISAM使用表锁,因此在写入正在进行时,整个表都被锁定,在超时事件中,它会覆盖某些内容导致数据丢失。
我们现在已将所有内容转移到MS SQL,因为InnoDB表现良好,但仍然不会重复使用已删除的文件空间,因此InnoDB不断增长。 MS SQL Express有10GB的限制,因此我们创建了4-8GB的页面并在那里存储blob。我们拥有自己的自定义复制,可以在网络上的三个服务器上复制文件,并具有相同的配置。
将文件存储在磁盘上是不好的,原因有很多,每个人都说文件系统设计用于高性能和可以存储数百万个文件,这是不正确的,当您拥有超过100,000个文件时,驱动器无法更快地执行。它们与一个大文件相比表现良好,然后是1000个较小的文件。目前,我们存储了1000万个文件,并将其存储在数据库中更有意义,因为数据库会对查询进行优化并具有良好的缓存。您可以在http://akashkava.com/blog/127/huge-file-storage-in-database-instead-of-file-system/阅读更多内容。
这正是MongoDb、Hadoop、Azure Blob Store、Haystack和Amazon S3被发明的原因。

2
我认为你首先需要确定是你的应用程序还是一些外部进程(备份/恢复?)更改了这些数据。实际上,如果文件应该保持不变,我看不出你的应用程序为什么需要更新这个图片(即使用相同的数据更新字段)。
一旦你找到了你的应用程序更新这个字段的哪些部分,你可能想发布一些代码,以查看是否存在任何转换、转义或其他操作。
如果像我所预料的那样,这种更新永远不应该发生,在表上设置一个BEFORE UPDATE触发器将允许你准确地知道问题发生的时间,并可能有助于识别可能的模式。比较OLD和NEW值,并在日志表中记录尽可能多的相关可用数据-注意,比较大的BLOB可能会影响性能,密切关注性能。

1

我的公司选择将图像存储在数据库之外。我们注意到像您正在使用的Blob一样,容易出现损坏和性能问题。我们在MSSQL、Sybase和Faircom中也看到了同样的问题。

每当应用程序需要访问图像时,它需要访问网络(或基于Web的)存储,以便找到该图像。然后数据仅存储图像的路径。

由于图像是文件系统中的一个平面文件,如果需要更新记录(例如添加描述图像的注释),则图像本身不会重新编译为Blob,并且不会有损坏的机会。


为什么同一张图片会反复出现这种情况呢 :( - Nikson Kanti Paul
你的程序是否会重写记录(包括 Blob 字段)? - CEPA

0

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