设为首页 收藏本站
查看: 3167|回复: 0

[经验分享] 使用MongoDB作为后台数据库的尝试

[复制链接]

尚未签到

发表于 2015-7-5 13:13:49 | 显示全部楼层 |阅读模式
  MongoDB作为一个阶层型数据库,在很短的时间里面是不可能被大面积推广使用的,
  本文作为一个实验性的课题,探讨一下MongoDB作为网站数据库的可能性。
  1.MongoDB作为代替关系型数据库的可能性。
  2.MongoDB作为代替文件服务器的可能性。
  通过探讨来加强对于MongoDB的认识
  
  环境准备
  技术选型
  1.由于是验证性质的课题,这里没有使用MVC5/6.如果有人对MVC6有兴趣,可以另开一个课题讨论。这里使用的是传统的WebForm。
  2.使用MongoDB最新版本作为数据库
  3.MongoDB的GUI使用本人自己开发的工具
  4.项目Host在阿里云上
  截图1.
  阿里云上的MongoDB数据库,使用本人开发工具看到的结果图
DSC0000.png
  
  5.启用  meishiyouji.com 域名
  6.UI使用的是  UIKit  这个库,进行响应式布局
DSC0001.png
  
  7.使用官方的MongoDB的C#驱动程序
  
  开发实践
  1.MongoDB作为阶层型数据库的代表,其最大特点就是Free的存储方式,一个Collection(相当于数据表)里面可以存储各种结构的文档。
  这个特性,如果用得好,非常有帮助,如果用的不好,容易造成混乱。例如我们可以将继承同一个基类的子类实例放在同一个Collection里面,
  这个是传统数据库无法做的,不同的子类的字段数会不一样,无法放到同一个表里面去,但是MongoDB是可以做到的。
  
  例子:
  基类如下
  例如一个行程的详细信息的基类如下:



  1 using Common.Database;
  2 using Common.Misc;
  3 using MongoDB.Bson.Serialization.Attributes;
  4 using MongoDB.Driver.Builders;
  5 using System.Collections.Generic;
  6
  7 namespace Common.Schedule
  8 {
  9     [BsonKnownTypes(typeof(Traffic), typeof(Visit), typeof(Shopping), typeof(Catering))]
10     public class DetailScheduleInfoBase : EntityBase
11     {
12         ///
13         /// 数据集名称
14         ///
15         public static string CollectionName = "DetailScheduleInfo";
16         ///
17         /// 序列号前缀
18         ///
19         public const string Prefix = "DI";
20         ///
21         ///
22         ///
23         public static string NewSN = Prefix + 0.ToString("D8");
24         ///
25         /// OverviewSN
26         ///
27         public string OverviewSN = string.Empty;
28         ///
29         /// DiarySchduleSN
30         ///
31         public string DiarySchduleSN = string.Empty;
32         ///
33         /// 说明
34         ///
35         public string Description = string.Empty;
36         ///
37         /// Visit
38         ///
39         public string Location = string.Empty;
40         ///
41         /// 移动开始时间
42         ///
43         public string FromTime = string.Empty;
44         ///
45         /// 移动终止时间
46         ///
47         public string ToTime = string.Empty;
48         ///
49         /// 分类
50         ///
51         public SchduleTypeEnum SchduleType = SchduleTypeEnum.Other;
52         ///
53         /// 图片
54         ///
55         public string UploadImgUrl = string.Empty;
56         ///
57         /// 分类中文
58         ///
59         ///
60         ///
61         public static string GetSchduleTypeEnumChinese(SchduleTypeEnum c)
62         {
63             string SchduleTypeEnumChinese = string.Empty;
64             switch (c)
65             {
66                 case SchduleTypeEnum.Traffic:
67                     SchduleTypeEnumChinese = "交通";
68                     break;
69                 case SchduleTypeEnum.Visit:
70                     SchduleTypeEnumChinese = "参观";
71                     break;
72                 case SchduleTypeEnum.Shopping:
73                     SchduleTypeEnumChinese = "购物";
74                     break;
75                 case SchduleTypeEnum.Catering:
76                     SchduleTypeEnumChinese = "饮食";
77                     break;
78                 //case SchduleTypeEnum.Rest:
79                 //    SchduleTypeEnumChinese = "休息";
80                 //    break;
81                 //case SchduleTypeEnum.Certificates:
82                 //    SchduleTypeEnumChinese = "手续";
83                 //    break;
84                 case SchduleTypeEnum.Other:
85                     SchduleTypeEnumChinese = "其他";
86                     break;
87                 default:
88                     break;
89             }
90             return SchduleTypeEnumChinese;
91         }
92         ///
93         /// 分类枚举
94         ///
95         public enum SchduleTypeEnum
96         {
97             ///
98             /// 交通
99             ///
100             Traffic,
101             ///
102             /// 参观
103             ///
104             Visit,
105             ///
106             /// 饮食
107             ///
108             Catering,
109             ///
110             /// 购物
111             ///
112             Shopping,
113             /////
114             ///// 休息
115             /////
116             //Rest,
117             /////
118             ///// 手续
119             /////
120             //Certificates,
121             ///
122             /// 其他
123             ///
124             Other
125         }
126         ///
127         /// 备注
128         ///
129         public string Comments = string.Empty;
130         ///
131         /// 是否为一个备选方案
132         ///
133         public bool IsBack = false;
134         ///
135         /// 消费记录
136         ///
137         public List ConsumptionList = new List();
138         ///
139         /// 添加详细行程
140         ///
141         ///
142         public static void Insert(DetailScheduleInfoBase info, string CreateUserName)
143         {
144             Operater.InsertRec(Prefix, CollectionName, info, CreateUserName);
145         }
146         ///
147         /// 通过序列号获得概要对象
148         ///
149         ///
150         public static DetailScheduleInfoBase GetDetailScheduleBySN(string DetailScheduleSN)
151         {
152             var query = Query.EQ("_id", DetailScheduleSN);
153             return Operater.GetFirstRec(CollectionName, query);
154         }
155         ///
156         /// 更新概要对象
157         ///
158         ///
159         public static void Update(DetailScheduleInfoBase newobj, string UserName)
160         {
161             Operater.UpdateRec(CollectionName, newobj, UserName);
162         }
163     }
164 }
  某一个子类的代码如下



1 namespace Common.Schedule
2 {
3     public class Traffic : DetailScheduleInfoBase
4     {
5         public Traffic()
6         {
7             SchduleType = SchduleTypeEnum.Traffic;
8         }
9         ///
10         /// 移动开始地址
11         ///
12         public string MoveFromLocation = string.Empty;
13         ///
14         /// 移动终止地址
15         ///
16         public string MoveToLocation = string.Empty;
17         ///
18         /// 交通手段
19         ///
20         public TrafficTypeEnum TrafficType = TrafficTypeEnum.Walk;
21         ///
22         /// 交通手段枚举
23         ///
24         public enum TrafficTypeEnum
25         {
26             ///
27             /// 步行
28             ///
29             Walk,
30             ///
31             /// 出租
32             ///
33             Taxi,
34             ///
35             /// 巴士
36             ///
37             Bus,
38             ///
39             /// 捷运
40             ///
41             JieYun,
42             ///
43             /// 台铁
44             ///
45             Train,
46             ///
47             /// 高铁
48             ///
49             SpeedTrain,
50             ///
51             /// 飞机
52             ///
53             AirPlane,
54             ///
55             /// 轮船
56             ///
57             Ship
58         }
59         ///
60         /// 交通手段中文
61         ///
62         ///
63         ///
64         public static string GetTrafficTypeEnumChinese(TrafficTypeEnum c)
65         {
66             string CurrencyEnumChinese = string.Empty;
67             switch (c)
68             {
69                 case TrafficTypeEnum.Walk:
70                     CurrencyEnumChinese = "步行";
71                     break;
72                 case TrafficTypeEnum.Taxi:
73                     CurrencyEnumChinese = "出租";
74                     break;
75                 case TrafficTypeEnum.Bus:
76                     CurrencyEnumChinese = "巴士";
77                     break;
78                 case TrafficTypeEnum.JieYun:
79                     CurrencyEnumChinese = "捷运";
80                     break;
81                 case TrafficTypeEnum.Train:
82                     CurrencyEnumChinese = "台铁";
83                     break;
84                 case TrafficTypeEnum.SpeedTrain:
85                     CurrencyEnumChinese = "高铁";
86                     break;
87                 case TrafficTypeEnum.AirPlane:
88                     CurrencyEnumChinese = "飞机";
89                     break;
90                 case TrafficTypeEnum.Ship:
91                     CurrencyEnumChinese = "轮船";
92                     break;
93                 default:
94                     break;
95             }
96             return CurrencyEnumChinese;
97         }
98     }
99 }
  
  基类和子类可以存放在同一个数据集里面
  1.注意 _t 字段,这里保存着基类的类型,如果是子类的话,这个字段是空的。
  2.编码的时候请注意,子类和基类 保存/序列化 的时候不会出现什么问题,但是读取/反序列化的时候,一定会出现错误。
  请在基类上增加这样的特性标签



1  [BsonKnownTypes(typeof(Traffic), typeof(Visit), typeof(Shopping), typeof(Catering))]
  这样的话,实际上是为系统注册了基类和子类关系,就可以正确的反序列化了。
DSC0002.png
  
  我们是否要将一个巨大的对象保存为一个数据文档(数据文档在MongoDB里面是一条记录的意思)
  一个巨大的对象,往往是一个树型结构的数据。
  例如,一个旅行行程表,它包含了一个行程概要,一个礼物列表,一个[每日行程列表],一个事前准备列表。
  每一个  [每日行程列表] 又包含了 【详细行程】列表,【详细行程】中又包含了【消费明细】等等。
  
DSC0003.png
  
  这样的话,一个行程就是一个巨大的对象(巨大的树形结构数据),当然,MongoDB是支持将整棵树作为一个数据文档,一下子保存到数据库中去的。
  (每一个数据文档的大小是有限制的,但是那个限制也是一个天文数字,我想没有人会把整部三国演义作为一个对象放到数据库里面去的吧)
  不过,这样对于数据库的管理将是一个巨大的灾难,这个灾难不仅仅是在数据进行更新时候,更新命令将会变得很复杂,而且会在各种聚合操作的时候,将会遍历所有的数据对象。
  这里使用了一种折衷的办法,将一个3层或者4层的结构,拆分为两个数据表,一个里面包含上面两层数据,然后做一个摘要结构,包含着下两层信息的一个简报。一些简单的信息直接从简版里面读取。
  下两层则放置在另一个数据库中。只有在需要详细数据的时候才读取出来呈现在界面上。
DSC0004.png
  
  这里的设计也就是一个数据存储的粒度的设计,粒度太小,就退化为关系型数据库。反之则每次更新将涉及整棵文档树。粒度设计是MongoDB的一个难点。
  
  2.将MongoDB作为文件服务器使用
  MongoDB的Grid File System特性就是一个内置的文件存储空间。你可以像管理数据一样管理文件。
DSC0005.png
  对于文件的读取,其实很简单
  C#驱动的Download和Upload方法提供了读取和保存文件的方法



1         ///
2         /// 保存文件
3         ///
4         ///
5         ///
6         ///
7         public static string InsertFile(HttpPostedFile file, string Username)
8         {
9             string Mongofilename = Username + "_" + System.DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + file.FileName;
10             MongoGridFS gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
11             gfs.Upload(file.InputStream, Mongofilename);
12             return Mongofilename;
13         }
14         ///
15         /// 获得文件
16         ///
17         ///
18         ///
19         public static void GetFile(Stream stream,string filename)
20         {
21             MongoGridFS gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
22             gfs.Download(stream,filename);
23         }
  
  当然,我们想做的事情是让IIS请求图片的时候,不走静态文件这条路,而是从数据库里面读取文件。
  这里我们要自定义 http Handler
  a.修改 webconfig
  注意,经典模式和集成模式是不同的
  我们让系统对于图片文件,用我们自定义的方法进行处理,JPG,GIF,PNG的响应使用ImageServer类来处理



  






  b.ImageServer



using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TaiWanTripPlanSite
{
public class ImageServer : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
var url = context.Request.Url.LocalPath.Split("/".ToCharArray());
var filename = url.Last();
///设置为最大时间,图片都是统一的文件名字,不会重复
context.Response.Expires = int.MaxValue;
Common.Database.Operater.GetFile(context.Response.OutputStream, filename);
}
}
}
  这里需要注意的是,一定要设定过期时间,不然的话,浏览器将不能使用缓存图片,每次访问同样的资源将进行全量下载。
  这里将过期时间设定为最大值,图片资源将永不过期。
  
  总结
  这篇文章只是提供了一个使用MongoDB作为数据库和文件服务的方案,对于性能没有任何的测试。
  阶层型数据库需要在粒度上进行总体的规划和设计。对于MongoDB,可以使用内置的文件服务功能,他对于同名文件,支持版本管理功能。
  如果有人愿意,可以进行一个压力测试,看一下MongoDB的性能如何。
  MongoDB的强大在于分布式,分片,副本等高级功能。这里只是抛砖引玉。
  欢迎和大家一起学习MongoDB,讨论MongoDB
  (本人已经接受英国某出版社的委托,作为志愿者Review MongoDB的视频课程)
  本文网站: 笔者花了一个星期的作品,使用UIKit组件,webform,mongodb数据库,仅供参考,让前端大牛见笑了。
  www.meishiyouji.com
  
  广告时间:
  如果你对我的技术认可,并且有志于从事前端开发,愿意和我一起共事,能否投个简历到  上海中和 软件公司
  Email:   mynightelfplayer@hotmail.com
  或者在 拉钩网 搜索   中和软件 的职位。
  http://www.lagou.com/gongsi/37348.html
  欢迎有2-5年前端经验的人应聘,我将会亲自带你们学习知识,提高技能,让你们实现自我价值。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-83375-1-1.html 上篇帖子: Mongodb源码分析--主程序入口main() 下篇帖子: MongoDB 客户端 MongoVue
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表