本文行文结构:
“大数据”认知
工业界对当前大数据的定义:大数据是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。
笔者最讨厌这种专业化的定义:就笔者思考而言就是很大的数据,其大超过了传统存储能力。沿着这个线索思考,那在数据库中什么能称为大数据呢?
笔者这样认为:所谓的“大数据”无非就是待存储数据信息比较大,此处的大是想对数据库中基本数据类型如:char,Integer,种类数据而言。
数据库如何保存数据“大数据信息”
当然以二进制形式保存~_~,(计算的世界只有0 1 ^-^)
而就mysql而言对大数据信息支持如下:
类型 | 长度 |
---|---|
tinyblob | 28--1B(256B) |
blob | 216-1B(64K) |
mediumblob | 224-1B(16M) |
longblob | 232-1B(4G) |
tinyclob | 28--1B(256B) |
clob | 216-1B(64K) |
mediumclob | 224-1B(16M) |
longclob | 232-1B(4G) |
存取案例:
数据库表信息:
CREATE TABLE tab_bin (
id INT PRIMARY KEY AUTO_INCREMENT, #文件标识
filename VARCHAR (100),#文件名
DATA MEDIUMBLOB #数据大小
) ;
配置文件信息:
driverClassName=com.mysql.jdbc.Driver
url=jdbc\:mysql\://localhost\:3306/gp?rewriteBatchedStatements\=true
username=root
password=root
工具类信息:
package com.test.data;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/***
* 连接数据库信息的工具类
* @author Adminstators
*
*/
public class JdbcUtils {
private static Properties props = null;//配置信息加载
//在加载时,读取配置文件中的信息
static {
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbconfig.properties");
props = new Properties();
try {
props.load(in);//装载配置信息
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//由于未借用任何第三方工具库,所以此时需要手动加载驱动信息
try {
Class.forName("com.mysql.jdbc.Driver");
}catch(ClassNotFoundException e) {
throw new RuntimeException("类加载驱动发生错误~~~~");
}
}
public static Connection getConnection() {
// 得到Connection
try {
return DriverManager.getConnection(props.getProperty("url"),
props.getProperty("username"),
props.getProperty("password"));
} catch (SQLException e) {
throw new RuntimeException("获取连接失败~~");
}
}
}
操作代码:
package com.test.data;
import java.awt.image.BufferedImageFilter;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.rowset.serial.SerialBlob;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
public class Data {
/***
* 像数据库中存放数据信息
* @throws SQLException
* @throws IOException
* @throws FileNotFoundException
*/
@Test
public void save( ) throws SQLException, FileNotFoundException, IOException {
Connection con = JdbcUtils.getConnection();
String sql = "insert into tab_bin values(?,?,?)";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setInt(1,1001);//设定编号信息
pstmt.setString(2, "secret");//设置文件名
/*//***
* 注意:
* “大数据”文件在数据库中存储时需要以Blob块的形式进行存放!
*/
// 把文件转换成byte[]
//提供文件所在地址
byte [] bytes = IOUtils.toByteArray(new FileInputStream("E:/Secret.mp3"));
Blob blob = new SerialBlob(bytes); //通过序列化Blob来得到Blob数据信息
pstmt.setBlob(3, blob);
pstmt.executeUpdate(); //执行sql模板信息
}
/***
* 从数据库中取出刚才存入的数据信息
借助第三方包:commons-io
*/
@Test
public void read() throws Exception {
Connection con = JdbcUtils.getConnection(); //获取连接
String sql = "select * from tab_bin where id = ?"; //查询数据
PreparedStatement prst = con.prepareStatement(sql); //获取sql模板信息
prst.setInt(1, 1001);
ResultSet rs = prst.executeQuery(); //获取结果集合
//循环读取结果集信息
while ( rs.next()) {
String fileName = rs.getString("filename");
System.out.println(fileName);
File file = new File("E:\\" + fileName + "_1" +".mp3");
Blob blob = rs.getBlob("data");//获取数据块信息
//获得到二进制文本流
BufferedInputStream in = new BufferedInputStream(blob.getBinaryStream());
FileOutputStream out = new FileOutputStream(file);
IOUtils.copy(in, out); //拷贝信息
}
}
}
执行结果:
下载后的信息 、数据库中保存的结果
执行中遇到的问题汇总:
第一:如果在保存中提示:
Packet for query is too large (6071393 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable.
根据提示信息很容易知道:明显是数据库待保存数据容量超过了默认保存数据的容量:
解决办法: 执行如下sql语句
SET GLOBAL max_allowed_packet = 2*1024*1024*10; #根据自己需求设定所需大小~~~
SHOW VARIABLES LIKE '%max_allowed_packet%';
第二:如果在执行读取操作是提示如下错误:
java.sql.SQLException:Before start of result set Before start of result set
这个错误其实和ResultSet结果集读取设定有关,ResultSet游标指向查询结果的前一行,即最开始的指向一个空行,这和sql,所以如果你不假如循环遍历结果的话,该问题不会得到解决~~~
解决办法:
//将数据的读取放入到 while 循环中 while ( rs.next()) { String fileName = rs.getString("filename"); System.out.println(fileName); File file = new File("E:\\" + fileName + "_1" +".mp3"); Blob blob = rs.getBlob("data"); System.out.println(blob.length()); BufferedInputStream in = new BufferedInputStream(blob.getBinaryStream()); FileOutputStream out = new FileOutputStream(file); IOUtils.copy(in, out); } /* 代码提示这个错误很可能是由于你觉得返回结果集仅为一行无需进行循环读取信息,所以就直接对结果集进行 数据处理。结果导致这个错误,将处理信息放置在循环中该问题就会解决 提示这个错误,很有可能你在使用原生的jdbc在开发,如果你使用继承框架 如mybatis , jdbcTemplate应该不会提示这样的错误~~~~ 想快速开发使用框架并无过错,想提升能力唯有不断coding基础内容~~~ */
总结
1.在向数据库保存数据信息时需要注意的地方在于:如何将数据信息转成Blob块存入数据库
把要存储的数据包装成Blob类型,然后调用PreparedStatement的setBlob()方法来设置数据,仅针对数据而言 setBlob的参数列表可以是一个输入流 一个 Blob对象,同时也可以使用提供一个Blob对象的方法来进行数据的读入
而Blob为一个接口无法进行实例化要进行的操作就是的实例化其一个子类SerialBlob来进行操作,SerialBlob的操作构造方法有两种一种是传入一个blob 一个是传入一个字节数组byte[]
所以借助 IOUtils.toByteArray()来进行处理得到一个字节数组 之后传入SerialBlob 依次来得到一个Blob数据对象
2.在读取数据库大数据信息时:需要注意的就是对于Blob数据块信息的处理操作
Blob blob = rs.getBlob("data"); //读取数据库中数据信息 byte[] datas = blob.getBytes(0, (int)blob.length()); //传入数据信息 FileUtils.writeByteArrayToFile(file, datas);//将字节数据信息写入到文件
注意:笔者在使用以上信息方式进行数据写入时一直提示:java.sql.SQLException: Position 'pos' can not be < 1
后来经过思考:发现在Blob中的getBytes()方法有如下的限制信息:
将信息改成blob.getBytes(1,(int)blob.length ) 后问题得到解决,同时从数据库中读入到的数据可以完整播放!
//Blob中 getBytes的源码信息 public synchronized byte[] getBytes(long pos, int length) throws SQLException { checkClosed(); //这就是报错的根源:笔者将数据的其实位置改为1后问题解决,可能这是JDK中规定Blob数据默认从1开始! if (pos < 1) { throw SQLError.createSQLException(Messages.getString("Blob.2"), //$NON-NLS-1$ SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } pos--; if (pos > this.binaryData.length) { throw SQLError.createSQLException("\"pos\" argument can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } if (pos + length > this.binaryData.length) { throw SQLError.createSQLException("\"pos\" + \"length\" arguments can not be larger than the BLOB's length.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); } byte[] newData = new byte[length]; System.arraycopy(getBinaryData(), (int) (pos), newData, 0, length); return newData; }
如果觉得以上方法解决可能存在安全隐患笔者提供第二种解决思路:
/* 笔者的第二种解决方式其实很简单: 通过流拷贝的形式进行数据的保存处理 首先获取到Blob的输入流,即将输入流定向到Blob块中 获取到文件的输出流位置 进行拷贝处理 */ //获取到Blob的字节流信息 BufferedInputStream in = new BufferedInputStream(blob.getBinaryStream()); FileOutputStream out = new FileOutputStream(file); //获取文件的输出流 IOUtils.copy(in, out);//拷贝信息
展望
数据库存放“大数据”可能其中还有很多知识需要去了解,本案例仅仅是笔者在开发毕业设计时所想到一些想法,受限于笔者眼界的狭隘。笔者认为其实百度云盘的设想可能也源于这样一个小案例,以下内容属于笔者一面之词,仅代表笔者个人言论,至于对错笔者并未考证,仅做猜想~~~
百度云的原理可能如下:百度公司在维系着一个大容量的数据仓库,在数据仓库中存放着我们所上传的数据信息,当我们需要下载时就从数据库中获取资源,之后传输到我们的pc端/手机端。
随着数据量的增大:处理数据时必须要面对的问题就是文件如何存放?当有用户检索某云盘信息时如何快速检索?这就是所技术发展中所面临的问题:
其实如果对大数据发展史有所了解的话就应该知道:其实谷歌公司早在2004年那会就发布了三篇论文:
分别是有关:分布式文件存储系统 (GFS),NoSql数据库,及大数据分布式计算框架 (mapReduce) 我所能想到的其实谷歌早在04年已经提出设想。论文奠定技术发展基石,业务催生技术不断突破,效率逼迫技术不断迭代更新我认为真技术发展领域的铁律。对于技术人而言只有洞察时代脉搏,才能收获珍贵知识和经验,当时代浪潮退去,在再某个方向努力,只会迷茫与压抑。