本文行文結構:
“大數據”認知
工業界對當前大數據的定義:大數據是指無法在一定時間範圍內用常規軟件工具進行捕捉、管理和處理的數據集合,是需要新處理模式才能具有更強的決策力、洞察發現力和流程優化能力的海量、高增長率和多樣化的信息資產。
筆者最討厭這種專業化的定義:就筆者思考而言就是很大的數據,其大超過了傳統存儲能力。沿着這個線索思考,那在數據庫中什麼能稱爲大數據呢?
筆者這樣認爲:所謂的“大數據”無非就是待存儲數據信息比較大,此處的大是想對數據庫中基本數據類型如: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年已經提出設想。論文奠定技術發展基石,業務催生技術不斷突破,效率逼迫技術不斷迭代更新我認爲真技術發展領域的鐵律。對於技術人而言只有洞察時代脈搏,才能收穫珍貴知識和經驗,當時代浪潮退去,在再某個方向努力,只會迷茫與壓抑。