mongodb海量數據CRUD優化


1. 批量保存優化

避免一條一條查詢,采用bulkWrite, 基於ReplaceOneModel,啟用upsert:

 public void batchSave(List<?> spoTriples, KgInstance kgInstance) {
        MongoConverter converter = mongoTemplate.getConverter();
        List<ReplaceOneModel<Document>> bulkOperationList = spoTriples.stream()
                .map(thing -> {
                    org.bson.Document dbDoc = new org.bson.Document();
                    converter.write(thing, dbDoc);
                    ReplaceOneModel<org.bson.Document> replaceOneModel = new ReplaceOneModel(
                            Filters.eq(UNDERSCORE_ID, dbDoc.get(UNDERSCORE_ID)), 
                            dbDoc,
                            new UpdateOptions().upsert(true));
                    return replaceOneModel;
                })
                .collect(Collectors.toList());
        mongoTemplate.getCollection(getCollection(kgInstance)).bulkWrite(bulkOperationList);
    }

2. 分頁優化

經常用於查詢的字段,需要確保建立了索引。

對於包含多個鍵的查詢,可以創建符合索引。

2.1 避免不必要的count

查詢時,走索引,速度並不慢,但是如果返回分頁Page<?>,需要查詢totalcount,當單表數據過大時,count會比較耗時,但是設想意向,你真的需要准確的數字嗎?

在google、百度等搜索引擎搜索關鍵詞時,只會給你有限的幾個結果,因此,我們也不必給出准確的數字,設定一個閾值,比如1萬,當我們發現總量大於1萬時,返回1萬,前端顯示大於1萬條即可。

原理也很鑒定啊,我們skip掉MAX_PAGE_COUNT,看是否還有數據,如果有就說明總量大於MAX_PAGE_COUNT,返回MAX_PAGE_COUNT即可,否則,計算真正的count。



int MAX_PAGE_COUNT = 10000;


/**
     * 當總數大於閾值時,不再計算總數
     *
     * @param mongoTemplate
     * @param query
     * @param collectionName
     * @return
     */
    private long count(MongoTemplate mongoTemplate, Query query, String collectionName) {
        query = query.with(PageRequest.of(MAX_PAGE_COUNT, 1));
        if (mongoTemplate.find(query, Thing.class, collectionName).size() > 0) {
            return MAX_PAGE_COUNT;
        }
        return mongoTemplate.count(query, collectionName);
    }
	

前端顯示:

大於10000

2.2 避免過多的skip

分頁不過避免需要先跳過一些數據,這個過程是需要消耗時間的,可以通過一個小技巧避免跳過。

比如,顯示列表時,排序為按最后修改時間倒序,每頁顯示100條,現在要顯示第100頁。
按照正常的做法,需要跳過99*100條數據,非常大的代價。換一個角度思考,因為數據是有序的,因此第100頁的數據的最后修改時間是小於第99頁最小的修改時間,查詢時加上這個條件,就可以直接取符合條件的前100條即可。

3. 全量導出優化

3.1 去掉不需要的字段

查詢時,指定真正有用的字段,這樣可以有效減少數據傳輸量,加快查詢效率。
例如:

 	    Query query = new Query();
        query.fields().include("_id").include("name").include("hot").include("alias");

3.2 避免使用findAll或者分頁查詢,改用stream

全量導出有兩個誤區,一是直接findAll,當數據量過大時,很容易導致服務器OutofMermory,就算沒有OOM,也會對服務器造成極大的負載,影響兄弟服務。另外,FindAll一次性加載數據到內存,整個速度也會比較慢,需要等待所有數據進入內存后才能開始處理。

另外一個誤區是,分頁查詢,依次處理。分頁查詢可以有效減少服務器負擔,不失為一種可行的方法。但是就和上面分頁說的那樣,分頁到后面的時候,需要skip掉前面的數據,存在無用功。稍微好一點的做法就是按照之前說的,將skip轉換為condtion,這種方式效率OK,但不推薦,存在代碼冗余。

            Page<Thing> dataList = entityDao.findAllByPage(kgDataStoreService.getKgCollectionByKgInstance(kg), page);
            Map<String, Individual> thingId2Resource = new ConcurrentHashMap<>();

            appendThingsToModel(model, concept2OntClass, hot, alias, dataList, thingId2Resource);

            while (dataList.hasNext()) {
                page = PageRequest.of(page.getPageNumber() + 1, page.getPageSize());
                dataList = entityDao.findAllByPage(kgDataStoreService.getKgCollectionByKgInstance(kg), page);
                appendThingsToModel(model, concept2OntClass, hot, alias, dataList, thingId2Resource);
            }

更推薦的做法是,采用mongoTemplate的steam方法,返回CloseableIterator迭代器,讀一條數據處理一條數據,實現高效處理:

@Override
	public <T> CloseableIterator<T> stream(final Query query, final Class<T> entityType, final String collectionName) {
		return doStream(query, entityType, collectionName, entityType);
	}

改用方法后,代碼可以更簡化高效:

  CloseableIterator<Thing> dataList = kgDataStoreService.getSimpleInfoIterator(kg);

            // 實體導入
            // Page<Thing> dataList = entityDao.findAllByPage(kgDataStoreService.getKgCollectionByKgInstance(kg), page);
            Map<String, Individual> thingId2Resource = new ConcurrentHashMap<>();

            appendThingsToModel(model, concept2OntClass, hot, alias, dataList, thingId2Resource);

待續。。。


作者:Jadepeng
出處:jqpeng的技術記事本--http://www.cnblogs.com/xiaoqi
您的支持是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。


免責聲明!

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



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