IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 19.MongoDB值distinct性能验证 -> 正文阅读

[大数据]19.MongoDB值distinct性能验证

前言:在利用mongodb进行消息记录存储中有一个相对棘手的问题那就是消息记录日历卡数据如何存。作为存储设计者肯定是希望日历数据能和消息记录存在一起,而不是额外引入一个db或者额外引入一张表。这里所说的日历卡大致如下,就使用体验来说如果发生过聊天就高亮可点击、否则就置灰。

1、整体思路

????????毋庸置疑,消息记录肯定是需要一张专门的表来存的。我们希望日历数据就在这张表的基础上加一个字段(如下表中的day)就能支持,而不是在引入一张表甚至引入其他数据库。关于消息记录的存储就简单理解如下模型即可。

account1
account2
time
dir
msg
day??

消息记录日历卡思路。

????????思路一:一个自然而然的想法就是对每条消息都带上day字段,用于记录这条消息的天时间戳;当需要的时候限定一个月起止时间的消息进行进行聚合或者distinct操作,即可得到一个月的去重day数据。

????????思路二:我们知道一个账号对一天可能收发很多消息,每条消息都带上day字段其实很没有必要;对思路一进行优化即每个账号对之间每天仅需要一条消息带上day字段记录天时间戳即可。更精确的就认为是每天的第一条数据带上即可。注:至于实现也很简单写消息记录的时候利用下redis缓存,确保每天只有第一条消息带day字段。

? ? ? ? 我们接下来做的事情就是验证distinct能够胜任方案二的拉取功能;以及索引对distinct的影响。

注:我们这里的消息记录日历卡不考虑消息撤回,因为我们业务场景的撤回并不是真正的撤回;对于真正会有消息撤回的场景需要考虑这个问题。

2、造数据脚本


如下为制造测试数据的脚本。


account1的取值范围为 2852000000 ~ 2852000099
account2的取值范围为 726045513175435_0 ~ 726045513175435_99
天时间戳的取值范围为 19000 ~ 19000 + 100
约定考察为期100天的数据;其中每天每个账号对发送100条消息;每条的第一条消息存改天的天时间戳;100条消息的发送间隔是10s一条,依照此频率凌晨就开始发。
?


account1_beg = 2852000000
account1_num = 100
account2_prefix = "726045513175435"
account2_end = 100

day_init = 19000
day_num = 100
day_msg_num = 100

for (var acc1 = account1_beg; acc1 < account1_beg+account1_num; acc1++){
    var docs = [];
    for (var acc2_index = 0; acc2_index < account2_end; acc2_index++){
        account1 = acc1.toString();
        account2 = account2_prefix + "_" + acc2_index.toString()
        for (var day = day_init; day < day_num + day_init; day ++){
            for (var msg_num = 0 ; msg_num < day_msg_num; msg_num++){
                time_sec = (86400 * day + 10 * msg_num);
                time_usec = time_sec * 1000000;
                subkey = time_sec.toString() + "_" + msg_num.toString();
                if (msg_num == 0){
                    docs.push({sacc:account1,bacc:account2,time:time_usec,subkey:subkey,day:day})
                }else{
                    docs.push({sacc:account1,bacc:account2,time:time_usec,subkey:subkey})
                }
            } 
        }
    }
    db.coll0.save(docs)
    print("finish ",account1)
}

-------------------------------------------------------------
当然想让各个字段更贴近真实场景的话可以把其他和索引无关的字段也补齐,如下:
-------------------------------------------------------------
account1_beg = 2852000000
account1_num = 10
account2_prefix = "726045513175435"
account2_end = 10

day_init = 19000
day_num = 100
day_msg_num = 100

types = "{\"class\":3,\"type\":1}\n"
msg = "CpYBEpMBCpABCo0B5a+55LiN6LW377yM5oiR5LiN5aSq5piO55m95oKo55qE5oSP5oCdfiDmgqjlj6/ku6Xlj5HpgIHmloflrZfigJzovazkurrlt6XigJ3ovazmjqXoh7Pkurrlt6XlrqLmnI3vvIzkurrlt6XlrqLmiLflnKjnur/ml7bpl7TvvJowOTowMCAtIDIxOjAw"
cont = "对不起,我不太明白您的意思~ 您可以发送文字“转人工”转接至人工客服,人工客户在线时间:09:00 - 21:00"
dir = 1
bkf = 0
skf = 2852199668
sort = 1
chan = 4

for (var acc1 = account1_beg; acc1 < account1_beg+account1_num; acc1++){
    for (var acc2_index = 0; acc2_index < account2_end; acc2_index++){
        var docs = [];
        account1 = acc1.toString();
        account2 = account2_prefix + "_" + acc2_index.toString()
        for (var day = day_init; day < day_num + day_init; day ++){
            for (var msg_num = 0 ; msg_num < day_msg_num; msg_num++){
                time_sec = (86400 * day + 10 * msg_num);
                time_usec = time_sec * 1000000;
                subkey = time_sec.toString() + "_" + msg_num.toString();
                if (msg_num == 0){
                    docs.push({sacc:account1,bacc:account2,time:time_usec,subkey:subkey,types:types,msg:msg,cont:cont,dir:dir,bkf:bkf,skf:skf,sort:sort,chan:chan,day:day})
                }else{
                    docs.push({sacc:account1,bacc:account2,time:time_usec,subkey:subkey,types:types,msg:msg,cont:cont,dir:dir,bkf:bkf,skf:skf,sort:sort,chan:chan})
                }
            } 
        }
        db.coll1.save(docs)
        print("finish ",account1," ",account2)
    }
}

3、验证指令及distinct操作

将上述脚本二的acc1和acc2数量分别调成10插入即100万条数据。对于每个固定的账号对数据组成和前面描述的完全一致。

(1)默认没有索引的执行情况

#2022-01-08 08:00:00   1641600000
#2022-02-01 08:00:00   1643673600

db.coll1.distinct("day",{"$and":[{time:{$gt:1641600000000000}},{time:{$lt:1643673600000000}},{sacc:"2852000005"},{bacc:"726045513175435_2"}]})

db.coll1.explain("executionStats").distinct("day",{"$and":[{time:{$gt:1641600000000000}},{time:{$lt:1643673600000000}},{sacc:"2852000005"},{bacc:"726045513175435_2"}]})

数据结果如下左图完全符合预期,但是能够感觉到明显的延迟。

执行explain可以看到其执行过程如下右图。即mongodb遍历了所有的100万条数据整个过程耗时574ms,显然这个性能是无法接受的。

?

(2) 接下来尝试对数据集建立索引然后看看执行效果。索引该怎么建呢??

思路一:建立{sacc:1,bacc:1,time:1}的索引 —— 预计遍历的数据会降低至一个账号对的目标时间范围内的数据,而不再是全部的100万条数据了。

db.coll1.createIndex({sacc:1,bacc:1,time:1})

db.coll1.distinct("day",{"$and":[{time:{$gt:1641600000000000}},{time:{$lt:1643673600000000}},{sacc:"2852000005"},{bacc:"726045513175435_2"}]})

db.coll1.explain("executionStats").distinct("day",{"$and":[{time:{$gte:1641600000000000}},{time:{$lte:1643673600000000}},{sacc:"2852000005"},{bacc:"726045513175435_2"}]})

?效果如下:完全符合预期。一个账号对一条100条消息,这里总共有24天;所以有2400条消息。

?

思路二:在上一步的基础上再加一个day字段看看会不会有更好的效果,即建立{sacc:1,bacc:1,time:1,day:1}的索引;或者{sacc:1,bacc:1,day:1}看看效果。

#删除前面建的index
db.coll1.dropIndex("sacc_1_bacc_1_time_1")
#建立包含day字段的索引
db.coll1.createIndex({sacc:1,bacc:1,time:1,day:1})

效果如下。看起来好像有效果,因为在仅有{sacc:1,bacc:1,time:1,day:1}的情况下确实也命中这个索引了。但总体感觉还是对目标数据的遍历,这一点没有任何改变;既然这样的话也就意义不大了。

另外也验证下仅有?{sacc:1,bacc:1,day:1}索引。不出意外的话就是在所有满足sacc和bacc的数据里面遍历。

#删除前面建的index
db.coll1.dropIndex("sacc_1_bacc_1_time_1_day_1")
#建立包含day字段的索引
db.coll1.createIndex({sacc:1,bacc:1,day:1})

?效果如下,完全符合预期。10000就是一个账号对的全部数据量。

?

三、结论

对被distinct的字段建立索引是没有作用的。我们能做的只是对前面的条件建立索引,让mongodb遍历的数据集尽可能的减少,仅此而已。

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-02-28 15:36:32  更:2022-02-28 15:37:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:32:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码