数据库与图片完美解决方案
目录
- 1. 背景
- 2. 解决思路
- 3. 解决方案
- 4. plugin 的开发与使用
- 5. 在事务中使用该插件
- 6. 通过触发器调用图片处理函数
1. 背景
我以电商网站为例,一般的网站产品数据存放在数据库中,商品图片是上传到文件服务器,然后通过http服务器浏览商品图片。这是最基本的也是最常见做法。
稍复杂的方案是,如果图片数量庞大,会使用分布式文件系统方案。但是这些方案都不能保证数据的完整性,极易产生脏数据(垃圾数据)。脏数据是指当你删除了数据库表中的记录后,图片仍然存在,或者手工删除了图片,而数据库中的记录仍然存在。
将图片放入数据库中存放在BLOB的方法可以解决脏数据问题,典型的案例是公安的身份证系统。但这种方案的前提是,图片不能太大,数量不多,访问量不大。 这显然不适合电商网站。
2009年我在走秀网工作,商品图片与缩图文件900GB到2012离职已经有10TB,每天有成百上千的商品上架下架,很多商品下架后永远不会再上架,这些批量下架的商品数据不会删除,仅仅标记为删除,总是期望以后能继续使用,实际上再也不会有人过问,另一方面随着品类经理频繁更换,员工离职,这些商品会石沉大海,再也无人问均。这些商品所对应的图片也就脏数据主要来源。新的品类经理上任后,会重新拍照,上传新图片。
总之,删除数据库中的数据不能将图片删除就会产生脏数据。很多采用删除数据的时候去检查图片如果存在先删除图片,再删除数据的方法。这种方案也非完美解决方案,存在这图片先被删除,程序出错SQL没有运行,或者反之。
2. 解决思路
如果删除图片能够成为事物处理中的一个环节,所有问题都能迎刃而解,可彻底解决脏数据的烦恼。
3. 解决方案
mysql plugin 开发 udf。我写几个function
UDF
- image_check(filename)
- 检查图片是否存在.
- image_remove(filename)
- 删除图片.
- image_rename(oldfile,newfile)
- 更改图片文件名.
- image_md5sum(filename)
- md5sum 主要用户图片是否被更改过.
- image_move(filename,filename)
- 移动图片的位置
有了上面的function后你就可以在begin,commit,rollback 直接穿插使用,实现在事物处理期间做你爱做的事。
4. plugin 的开发与使用
编译UDF你需要安装下面的软件包
sudo apt-get install pkg-config
sudo apt-get install libmysqlclient-dev
sudo apt-get install gcc gcc-c++ make automake autoconf
https://github.com/netkiller/mysql-image-plugin
编译udf,最后将so文件复制到 /usr/lib/mysql/plugin/
git clone https://github.com/netkiller/mysql-image-plugin.git
cd mysql-image-plugin/src
gcc -I/usr/include/mysql -I./ -fPIC -shared -o image.so image.c
sudo mv image.so /usr/lib/mysql/plugin/
装载
create function image_check returns boolean soname 'images.so';
create function image_remove returns boolean soname 'images.so';
create function image_rename returns boolean soname 'images.so';
create function image_md5sum returns string soname 'images.so';
create function image_move returns string soname 'images.so';
卸载
drop function image_check;
drop function image_remove;
drop function image_rename;
drop function image_md5sum;
drop function image_move;
5. 在事务中使用该插件
插入图片流程,上传图片后,通过插件检查图片是否正确上传,然后插入记录
begin;
IF image_check('/path/to/images.jpg') THEN
insert into images(product_id,thumbnail,original) values(1000,'thumbnail/path/to/images.jpg','original/path/to/images.jpg');
commit;
ELSE
image_remove('/path/to/images.jpg');
END IF
rollback;
删除商品采用image_move 方案,当出现异常rollback后还可以还原被删除的图片
begin;
IF image_check('/path/to/images.jpg') THEN
select thumbnail,original into @thumbnail,@original from images where id='1000' for delete;
delete from images where id='1000';
select image_move(@thumbnail,'recycle/path/to/');
select image_move(@original,'recycle/path/to/');
commit;
END IF
rollback;
select image_move('recycle/path/to/images.jpg','path/to/images.jpg');
我们可以使用EVENT定时删除回收站内的图片
image_remove('recycle/path/to/images.jpg');
6. 通过触发器调用图片处理函数
通过触发器更能保证数据完整性
1. insert 触发器的任务: 插入记录的时候通过image_check检查图片是否正常上传,如果非没有上传,数据插入失败。
2. delete 触发器的任务: 检查删除记录的时候,首先去删除图片,删除成功再删除该记录。
触发器进一步优化
1. insert 触发器的任务: 插入记录的时候通过image_check检查图片是否正常上传,如果非没有上传,数据插入失败。如果上传成功再做image_md5sum 进行校验100% 正确后插入记录
2. delete 触发器的任务: 检查删除记录的时候,首先去改图片文件名,然后删除该记录,最后删除图片,删除成功。如果中间环境失败 记录会rollback,图片会在次修改文件名改回来。100% 保险
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法