海量數據的解決方案


 

1. 緩存和頁面靜態化
  數據 量大 這個 問題 最 直接 的 解決 方案 就是 使用 緩存, 緩存 就是 將從 數據庫 中 獲取 的 結果 暫時 保存 起來, 在下 次 使用 的 時候 無需 重新 到 數據庫 中 獲取, 這樣 可以 大大 降低 數據庫 的 壓力。

  緩存的使用方式可以分為通過程序直接保存到內存中和使用緩存框架兩種方式。 程序 直接 操作 主要 是 使用 Map, 尤其是 ConcurrentHashMap, 而 常用 的 緩存 框架 有 Ehcache、 Memcache 和 Redis 等。 緩存 使用 過程中 最重要 問題是 什么時候 創建 緩存 和 緩存 的 失效 機制。 緩存 可以 在 第一次 獲取 的 時候 創建 也可以在 程序 啟動 和緩 存 失效 之后 立即 創建, 緩存 的 失效 可以 定期 失效, 也可 以在 數據 發生 變化 的 時候 失效, 如果 按 數據 發生 變化 讓 緩存 失效, 還可以 分 粗 粒度 失效 和 細粒 度 失效。

  不過緩存也不是什么情況都適用, 它主要用於數據變化不是很頻繁的情況。 而且如果是定期失效( 數據 修改 時不 失效) 的 失效 機制, 實時性要求也不能太高, 因為這樣緩存中的數據和真實數據可能會不一致。 如果 是 文章 的 評論 則 關系 不是 很大, 但如 果是 企業 業務 系統 中 要 生成 報表 的 數據 則 問題 就 大 了。

  跟緩存相似的另外一種技術叫頁面靜態化, 它在原理上跟緩存非常相似, 緩存是將從數據庫中獲取到的數據( 當然 也可以 是 別的 任何 可以 序列 化 的 東西) 保存起來, 而頁面靜態化是將程序最后生成的頁面保存起來, 使用頁面靜態化后就不需要每次調用 都 重新生成頁面了, 這樣不但不需要查詢數據庫, 而且連應用程序處理都省了, 所以頁面靜態化同時對數據量大和並發量高兩大問題都有好處。

  頁面靜態化可以在程序中使用模板技術生成, 如常用的Freemarker 和 Velocity 都可以 根據 模板 生成 靜態 頁面, 另外 也可以 使用 緩存 服務器 在 應用 服務器 的 上一 層 緩存 生成 的 頁面, 如 可以 使用 Squid, 另外 Nginx 也 提供 了 相應 的 功能。

2 .數據庫優化

  要解決數據量大的問題, 是避不開數據庫 優化 的。 數據庫 優化 可 以在 不 增加 硬件 的 情況下 提高 處理 效率, 這是 一種 用 技術 換 金錢 的 方式。 數據庫 優化 的 方法 非常 多, 常 用的 有 表 結構 優化、 SQL 語句 優化、 分區 和 分表、 索引 優化、 使用 存儲 過程 代替 直接 操作 等, 另外 有時候 合理 使用 冗余 也能 獲得 非常 好的 效果。

表結構優化

  表結構優化是數據庫 中最 基礎 也是 最重要的, 如果 表 結構 優化 得不 合理, 就可能 導致 嚴重 的 性能 問題, 具體 怎么 設計 更 合理 也沒 有 固定 不變 的 准則, 需要 根據 實際情況 具體 處理。

SQL語句優化

  SQL語句優化 也是 非常 重要的, 基礎 的 SQL 優化 是 語法 層面 的 優化, 不過更重 要的 是 處理 邏輯 的 優化, 這也 需要 根據 實際情況 具體 處理, 而且 要和 索引 緩存 等 配合 使用。 不過 SQL 優化 有一個 通用 的 做法 就是, 首先 要將 涉及 大 數據 的 業務 的 SQL 語句 執行 時間 詳細 記錄 下來, 其次 通過 仔細 分析 日志( 同一 條 語句 對不 同 條件 的 執行 時間 也可能 不同, 這點 也需 要 仔細 分析) 找出 需要 優化 的 語句 和 其中 的 問題, 然后 再 有的放矢 地 優化, 而 不是 不分 重點 對 每條 語句 都 花 同樣 的 時間 和 精力 優化。

分區

  當數據量變多的時候, 如果 可以 分區 或者 分表, 那將 起到 非常 好的 效果。 當 一張 表中 的 數據 量變 多的 時候 操作 速度 就 慢了, 所以 很容易 想到 的 就是 將 數據 分到 多個 表中 保存, 但是 這么 做 之后 操作 起來 比較 麻煩, 想 操作( 增 刪改 查) 一個 數據 還 需要 先 找到 對應 的 表, 如果 涉及 多個 表 還得 跨 表 操作。 其實 在 常用 的 數據庫 中 可以 不分 表 而 達到 跟 分表 類似 的 效果, 那就 是 分區。 分區 就是 將 一張 表中 的 數據 按照 一定 的 規則 分到 不同 的 區 來 保存, 這樣 在 查詢 數據 時 如果 數據 的 范圍 在 同一個 區內 那么 可以 只對 一個 區 的 數據 進行 操作,這樣 操作 的 數據 量 更少, 速度 更快, 而且 這種 方法 對 程序 是 透明 的, 程序 不需 要做 任何 改動。

分表

  如果一張表中的 數據 可以 分為 幾種 固定 不變 的 類型, 而且 如果 同時 對 多種 類型 共同 操作 的 情況 不多, 那么 都可以 通過 分表 來 處理, 這也 需要 具體 情況 具體 對待。 筆者 之前 對 一個 業務 系統 進行 重 構 開發 時 就 將其 中 保存 工人 工作 卡片 的 數據表 分成 了 三個 表, 並且 對 每個 表 進行 分區, 在 同時 使用 緩存( 主要 用於 在 保存 和 修改 時 對 其他 表 的 數據 獲取 中, 如 根據 工人 Id 獲取 工人 姓名、 工人 類別、 所在單位、 所在 工段 及 班組 等 信息)、 索引、 SQL 優化 等 的 情況下 操作 速度 比 原來 提高 了 100 倍 以上。 那時 的 分表 是按 照 工作 卡片 的 類型 來 划分 的, 因為 當時的 要求 是要 保留 所有 的 記錄。 比如, 修改 了 卡片 的 信息, 則需 要 保存 是 誰在 什么時候 對 卡片 進行 修改, 修改 前 的 數據 是什么, 添加 刪除 也 一樣, 這種 需求 一般 的 做法 就是 用 一個 字段 來做 卡片 狀態 的 標志 位, 將 卡片 分成 不同 的 類型。 不過 這里 由於 數據 量 非常 大 所以 就 將 卡片 分別 保存 到了到了 三個 表中, 第一個 表 保存 正常 卡片, 第二個 表 保存 刪除 后的 卡片, 第 三個 表 保存 修改 之前 的 卡片, 並且 對 每個 表 都 進行 了 分區。 由於 報表 一般 是按 月份、 季度、 半年 和 年 來做 的, 所以 分區 是按 月份 來 分 的, 每個 月 一個 分區, 這樣 問題 就 解決 了。 當然 隨着 時間 的 推移, 如果 總 數據 量 達到 一定程度, 還需 要 進一步 處理。

  另外 一種 分表 的 方法 是將 一個 表中 不同 類型 的 字段 分到 不同 的 表中 保存, 這么 做 最 直接 的 好處 就是 增刪 改 數據 的 時候 鎖定 的 范圍 減小 了, 沒被 鎖定 的 表中 的 數據 不受影響。 如果 一個 表 的 操作 頻率 很高, 在 增刪 改 其中 一部分 字段 數據 的 同時 另一 部分 字段 也可 能被 操作, 而且( 主要 指 查詢) 用 不到 被 增 刪改 的 字段, 那么 就可以 把 不同 類型 的 字段 分別 保存 到 不同 的 表中, 這樣 可以 減少 操作 時 鎖定 數據 的 范圍。 不過 這樣 分表 之后, 如果 需要 查詢 完整 的 數據 就得 使用 多 表 操作 了。

索引優化

  索引的大致原理 是在 數據 發生 變化( 增刪 改) 的 時候 就 預先 按指 定 字段的 順序 排列 后 保存 到 一個 類似 表 的 結構 中, 這樣 在 查找 索引 字段 為 條件 的 記錄 時 就可以 很快 地 從 索引 中 找到 對應 記錄 的 指針 並從 表中 獲取 到 記錄, 這樣 速度 就 快 多了。 不過 索引 也是 一把 雙刃 劍, 它在 提高 查詢 速度 的 同時 也 降低 了 增 刪改 的 速度, 因為 每次 數據 的 變化 都 需要 更新 相應 的 索引。 不過 合理 使用 索引 對 提升 查詢 速度 的 效果 非常 明顯, 所以 對 哪些 字段 使用 索引、 使用 什么 類型 的 索引 都 需要 仔細 琢磨, 並且 最好 再做 一些 測試。

使用存儲過程代替直接操作

  在操作過程復雜而且調用頻率高的業務中, 可以 通過 使用 存儲 過程 代替 直接 操作 來 提高效率, 因為 存儲 過程 只需 要 編譯 一次, 而且 可以 在 一個 存儲 過程 里面 做 一些 復雜 的 操作。

  上面 這些 就是 經常 用到 的 數據庫 優化 的 方法, 實際 環境 中 怎么 優化 還得 具體 情況 具體 分析。 除了 這些 優化 方法, 更重 要的 是 業務 邏輯 的 優化。

3.分離活躍數據

  雖然有些數據總數據量非常大, 但是活躍數據並不多, 這種情況就可以將活躍數據單獨保存起來從而提高處理效率。 比如, 對 網 站 來說, 用戶 很多 時候 就是 這種 數據, 注冊 用戶 很多, 但是 活躍 用戶 卻不 多, 而 不 活躍 的 用戶 中有 的 偶爾 也會 登錄 網 站, 因此 還不 能 刪除。 這時 就可以 通過 一個 定期 處理 的 任務 將不 活躍 的 用戶 轉移 到 別的 數據 表中, 在 主要 操作 的 數據表 中 只 保存 活躍 用戶, 查詢 時 先 從 默認 表中 查找, 如果 找 不到 再 從不 活躍 用戶 表中 查找, 這樣 就可以 提高 查詢 的 效率。 判斷 活躍 用戶 可以 通過 最近 登錄 時間, 也可以 通過 指定 時間 段 內 登錄 次數。 除了 用戶 外 還有 很多 這種 類型 的 數據, 如 一個 網 站上 的 文章( 特別是 新聞 類 的)、 企業 業務 系統 中 按時 間 記錄 的 數據 等。

4.批量讀取和延遲修改

  批量讀取和延遲修改的原理是通過減少操作的次數來提高效率, 如果 使用 得 恰當, 效率 將會 呈 數量級 提升。 批量 讀取 是將 多次 查詢 合並 到 一次 中 進行, 比如, 在 一個 業務 系統 中 需要 批量 導入 工人 信息, 在 導入 前 需要 檢查 工人 的 編碼 是否 已經 在 數據庫 中、 工人 對應 的 部門 信息 是否 正確( 在 部門 表中 是否 存在)、 工人 的 工種 信息 在 工種 表中 是否 存在 等, 如果 每 保存 一條 記錄 都 查詢 一次 數據庫, 那么 對 每個 需要 檢查 的 字段, 都 需要 查詢 與 要 保存 的 記錄 條數 相同 次數 的 數據庫, 這時 可以 先 將 所有 要 保存 的 數據 的 相應 字段 讀取 到 一個 變量 中, 然后 使用 in 語句 統一 查詢 一次 數據庫, 這樣 就可以 將 n( 要 保存 記錄 的 條數) 次 查詢 變為 一次 查詢 了。 除了 這種 對 同一個 請求 中的 數據 批量 讀取, 在 高 並發 的 情況下 還可以 將 多個 請求 的 查詢 合並 到 一次 進行, 如 將 3 秒 或 5 秒 內 的 所有 請求 合並 到一起 統一 查詢 一次 數據庫, 這樣 就可以 有效 減少 查詢 數據庫 的 次數, 這種 類型 可以 用 異步 請求 來 處理。

  延遲修改主要針對高並發而且頻繁修改(包括新增)的數據, 如一些統計數據。 這種情況可以先將需要修改的數據暫時保存到緩存中, 然后定時將緩存中的數據保存到數據庫中, 程序在讀取數據時可以同時讀取數據庫中和緩存中的數據。 這里的緩存和前面 介紹 的緩存有本質的區別, 前面的緩存在使用過程中, 數據庫中的數據一直是最完整 的, 但這里數據庫中的數據會有一段時間不完整。 這種方式下如果保存緩存的機器出現了問題將可能會丟失數據, 所以如果是重要的數據就需要做一些特殊處理。 筆者 之前 所在 的 單位 有一個 系統 需要 每月 月末 各廠 分別 導入 自己 廠 當月 的 相應 數據, 每到 月末 那個 系統 就 處於 基本 癱瘓 的 狀態 了, 而且 各廠 從 整理 出 數據 到 導入 系統 只有 幾天 的 時間, 所以 有的 廠 就 專門 等 晚上 人少 的 時候 才 進行 操作, 對於 這種 情況 就可 采用 延遲 修改 的 策略 來 解決。

5.讀寫分離
  讀寫分離的本質是對數據庫進行集群, 這樣就可以在高並發的情況下將數據庫的操作分配到多個數據庫 服務器去處理從而降低單台服務器的壓力, 不過由於數據庫的特殊性—— 每 台 服務器 所 保存 的 數據 都 需要 一致, 所以 數據 同步 就成 了 數據庫 集群 中最 核心 的 問題。 如果 多台 服務器 都可以 寫 數據 那么 數據 同步 將 變得 非常 復雜, 所以 一般 情況下 是將 寫 操作 交給 專門 的 一台 服務器 處理, 這 台 專門 負責 寫的 服務器 叫做 主 服務器。 當 主 服務器 寫入( 增刪 改) 數據 后 從 底層 同步 到 別的 服務器( 從 服務器), 讀 數據 的 時候 到 從 服務器服務器 讀取, 從 服務器 可以 有 多台, 這樣 就可以 實現 讀寫 分離, 並且 將 讀 請求 分配 到 多個 服務器 處理。 主 服務器 向 從 服務器 同步 數據 時, 如果 從 服務器 數量 多, 那么 可以 讓 主 服務器 先向 其中 一部分 從 服務器 同步 數據, 第一 部分 從 服務器 接收 到數 據 后再 向 另外 一部分 同步, 這時 的 結構 如圖 1- 5 所示。

 

 

6.分布式數據庫
  分布式數據庫是將不同的表存放到不同的數據庫中然后再放到不同的服務器。 這樣在處理請求時, 如果需要調用多個表, 則可以讓多台服務器同時處理, 從而提高處理速度。

  數據庫集群(讀寫分離)的作用是將 多個 請求 分配 到 不同 的 服務器 處理, 從而 減輕 單 台 服務器 的 壓力, 而 分布式 數據庫 是 解決 單個 請求 本身 就 非常 復雜 的 問題, 它 可以 將 單個 請求 分配 到 多個 服務器 處理, 使用 分布式 后的 每個 節點 還可以 同時 使用 讀寫 分離, 從而 組成 多個 節點 群, 結構圖 如圖 1- 6 所示。

 

 

 

  實際 使用 中 分布式 數據庫 有很 多 復雜 的 問題 需要 解決, 如 事務處理、 多 表 查詢 等。 分布式 的 另外 一種 使用 的 思路 是將 不同 業務 的 數據表 保存 到 不同 的 節點, 讓 不同 的 業務 調用 不同 的 數據庫, 這種 用法 其 實是 和 集群 一樣 起 分流 的 作用, 不過 這種 情況 就不 需要 同步 數據 了。 使用 后面 這種 思路 時 架構 還是和 上面 圖中 的 一樣, 所以 技術 和 架構 只是 一個 工具, 真正 重要的 是 思路, 也就是 工具 的 使用方法。
7.NoSQL和Hadoop

  NoSQL 是 近年來 發展 非常 迅速 的 一項 技術, 它的 核心 就是 非 結 構化。 我們 一般 使用 的 數據庫( SQL 數據庫) 都是 需要 先 將 表 的 結構 定義 出來, 一個 表 有幾個 字段, 每個 字段 各 是什么 類型, 然后 才能 往里 面 按照 相應 的 類型 保存 數據, 而且 按照 數據庫 范式 的 規定, 一個 字段 只能 保存 單一 的 信息, 不可以 包括 多層 內容, 這就 對使 用的 靈活性 帶來 了 很大 的 制約, NoSQL 就是 突破 了 這些 條條框框, 可以 非常 靈活 地 進行 操作, 另外 因為 NoSQL 通過 多個 塊 存儲 數據 的 特點, 其 操作 大 數據 的 速度 也 非常 快, 這些 特性 正是 現 在的 互 聯網 程序 最 需要 的, 所以 NoSQL 發展 得 非常 快。 現在 NoSQL 主要 使 用在 互 聯網 的 程序 中, 在 企業 業務 系統 中 使用 的 還不 多, 而且 現在 NoSQL 還 不是 很 成熟, 但 由於 靈活 和 高效 的 特性, NoSQL 發展 的 前景 是非 常 好的。

  Hadoop 是 專門 針對 大 數據處理 的 一套 框架, 隨着 近年來 大 數據 的 流行 Hadoop 也 水漲船高, 出世 不久 就 紅得發紫。 Hadoop 對 數據 的 存儲 和 處理 都 提供 了 相應 的 解決 方案, 底層 數據 的 存儲 思路 類似於 1. 4. 6 節 介紹 的 分布式 加 集群 的 方案, 不過 Hadoop 是將 同一個 表中 的 數據 分成 多塊 保存 到 多個 節點( 分布式), 而且 每一 塊 數據 都有 多個 節點 保存( 集群), 這里 集群 除了 可以 並行 處理 相同 的 數據, 還可以 保證 數據 的 穩定性, 在其中 一個 節點 出現 問題 后 數據 不會 丟失。 這里 的 每個 節點 都不 包含 一個 完整 的 表 的 數據, 但是 一個 節點 可以 保存 多個 表 的 數據, 結構圖 如圖 1- 7 所示。

 

  Hadoop 對 數據 的 處理 是 先 對 每 一塊 的 數據 找到 相應 的 節點 並進 行 處理, 然 后再 對 每一個 處理 的 結果 進行 處理, 最后 生成 最終 的 結果。 比如, 要 查找 符合 條件 的 記錄, Hadoop 的 處理 方式 是 先 找到 每 一塊 中 符合 條件 的 記錄, 然后 再將 所有 獲取 到 的 結果 合並 到一起, 這樣 就可以 將同 一個 查詢 分到 多個 服務器 處理, 處理 的 速度 也就 快了, 這一點 傳統 的 數據庫 是 做不到 的。

來自:韓路彪 著. 看透Spring MVC:源代碼分析與實踐 (Web開發技術叢書)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM