簡介
在使用 Java 對數據庫進行連接時,都會獲取到一個 cursor ,cursor 實際指到的是我們查詢數據庫的query,而並不是 query 查詢到的數據集。
此次在使用 mongo 的 cursor 的過程中,對線上數據庫產生了很大壓力,在這里對此次的優化過程進行記錄。
場景
數據源:Mongo 數據庫 4台服務器 4000+表 總共3億+數據量
背景介紹:即將建立大數據平台,需要將數據源的數據導入到 hbase 中,分為歷史數據導入和實時 opLog 數據導入兩部分
方案:
- 多線程歷史數據導入
- 多線程實時數據導入
問題描述:
- 導入的數據占據資源
- 硬盤資源 計算需要占用的硬盤資源,事先確認硬盤資源是否充足
- CPU 確認運行腳本的服務器處於半空閑狀態
- 內存 運行jar包時的腳本配置內存即可
- 對線上數據庫的連接沒有釋放,導致線上數據庫壓力
- 歷史數據導入時,每個線程都會新建連接,之后關閉 #可能沒有關閉
- 實時數據導入時,輪詢產生連接,但是關閉時間較長,導致的連接數增長 #關閉時長較長
解決方法:
- 歷史數據導入時的每個線程所建立的連接,修改邏輯,使其一定被關閉
- 實時數據導入時的每個線程的讀取 opLog 的 cursor ,使其保持活性
- 創建連接池,確保連接數在一定時間內
知識點
cursor
cursor的獲取:find() 方法返回的是一個 FindIterable
1)batchSize(int size):每次網絡請求返回的document條數,比如你需要查詢500條數據,mongodb不會一次性全部load並返回給client,而是每次返回batchSize條,遍歷完之后后再通過網路IO獲取直到cursor耗盡。默認情況下,首次批量獲取101個document或者1M的數據,此后每次4M,當然我們可以通過此方法來覆蓋默認值,如果文檔尺寸較小,則建議batchSize可以大一些。
2)skip(int number)、limit(int number):同SQL中的limit字句,即表示在符合匹配規則的結果集中skip一定數量的document,並最終返回limit條數據。可以實現分頁查詢。
3)maxTime(int time,TimeUnit unit):表示此次操作保持的最長時間,即server端保持cursor狀態的最長時間,如果超時server端將移除此cursor,即再次通過此cursor遍歷數據將會error。
4)sort(Bson bson):根據指定field排序,參與排序的字段最好是索引,如果不是,將會在內存中排序,如果參與排序的數據尺寸大於32M,將會拋出error。1表示正序,-1表示倒敘,比如"age":1表示按照age正序排序。
5)noCursorTimeout(boolean timeout):如果cursor空閑一定時間后(10分鍾),server端是否將其移除,默認為false,即server會將空閑10分鍾的cursor移除以節約內存。如果為true,則表示server端不需要移除空閑的cursor,而是等待用戶手動關閉。無論如何,開發者都需要注意,手動關閉cursor。
6)partial(boolean partial):對於sharding集群,如果一個或者多個shard不可達,是否允許返回部分數據(只從正常的shard中獲取數據)。
7)cursorType():指定cursor類型,當cursor遍歷完畢后是否關閉cursor,默認是關閉,無論何時都建議手動關閉cursor(不管是否耗盡curosr);當然有些開發場景可能需要保持cursor的活性,遍歷到cursor的最后一條后,不關閉cursor,繼續等待,此后一段時間內如果有新數據插入到cursor之后,則可以繼續遍歷,這就是Tailable Cursor,通常對於Capped Collection中使用。目前支持支持3種類型的Cursor:NonTailable、Tailable、TailableAwait。
8)projection(Bson bson):限定返回結果中需要包含的filed或者數組元素。在6)中我們已經看到相關的幾個例子。默認情況下,將會返回document的所有字段,1表示包含,0表示不包含。
連接池
Mongo Client本身即是一個連接池,有默認參數,也可以自己設置參數,建議全局使用同一個mongo的client實例。
NOTE: 連接池里的連接數 = ConnectionsPerHost * threadsAllowedToBlockForConnectionMultiplier
參數說明:來自於http://api.mongodb.com/java/3.2/
com.mongodb
Class MongoClientOptions.Builder
參數名 | 默認參數值 | 參數說明 |
---|---|---|
ConnectionsPerHost | 100 | 設置每台主機的最大連接數 |
Description | null | |
RequiredReplicaSetName | null | 設置群集所需的副本集名稱 |
ConnectTimeout | 10000 | 設置連接超時 |
HeartbeatConnectTimeout | 20000 | 設置用於群集心跳的連接的連接超時 |
HeartbeatFrequency | 10000 | 設置心跳頻率。這是驅動程序嘗試確定群集中每個服務器當前狀態的頻率。默認值為10,000毫秒 |
HeartbeatSocketTimeout | 20000 | 設置用於群集心跳的連接的套接字超時 |
LocalThreshold | 15 | 設置本地閾值 |
MaxConnectionIdleTime | 0 | 設置池連接的最大空閑時間 |
MaxConnectionLifeTime | 0 | 設置池連接的最長生存期 |
MaxWaitTime | 120000 | 設置線程阻塞等待連接的最長時間 |
MinConnectionsPerHost | 0 | 設置每台主機的最小連接數 |
MinHeartbeatFrequency | 500 | 設置最小心跳頻率。如果驅動程序必須頻繁地重新檢查服務器的可用性,它至少會在上次檢查后等待這么長時間,以避免浪費精力。默認值為500毫秒 |
ServerSelectionTimeout | 30000 | 以毫秒為單位設置服務器選擇超時,這定義了驅動程序在引發異常之前等待服務器選擇成功的時間。 值0表示如果沒有服務器可用,它將立即超時。負值意味着無限期等待。 |
SocketTimeout | 0 | 設置socket超時 |
threadsAllowedToBlockForConnectionMultiplier | 5 | 設置允許阻塞等待連接的線程數的乘數。 |
socketKeepAlive | 設置是否啟用套接字保持活動 | |
sslEnabled | 設置是否使用SSL。將此設置為true也會將SocketFactory設置為sLSocketFactory。GetDefault ( )並將此設置為false會將SocketFactory設置為SocketFactory。GetDefault ( ) | |
sslInvalidHostNameAllowed | 定義是否允許無效主機名。默認為false。在將此設置為真之前要小心,因為這會使應用程序容易受到中間人攻擊 | |
readPreference | 設置讀取首選項 | |
writeConcern | 設置寫入關注點。 | |
readConcern | 設置讀取關注點。 | |
codecRegistry | 設置編解碼器注冊表 請注意,DB和DBCollection的實例不使用注冊表,因此無需在注冊表中包含DBObject的編解碼器。 |
|
addCommandListener | 添加給定的命令偵聽器。 | |
socketFactory | 設置socket工廠。 | |
cursorFinalizerEnabled | 設置是否啟用游標終結器 | |
alwaysUseMBeans | 設置驅動程序注冊的JMX Beans是否應該始終是MBean,而不管VM是Java 6還是更高版本。如果為false,如果虛擬機為Java 6或更高版本,驅動程序將使用MXBeans,如果虛擬機為Java 5,則使用mbean。 | |
dbDecoderFactory | 設置解碼器工廠 | |
dbEncoderFactory | 設置編碼器工廠 | |
legacyDefaults | 將默認值設置為MongoOptions中的值 |