MongoDB作为一个阶层型数据库,在很短的时间里面是不可能被大面积推广使用的,

本文作为一个实验性的课题,探讨一下MongoDB作为网站数据库的可能性。

1.MongoDB作为代替关系型数据库的可能性。

2.MongoDB作为代替文件服务器的可能性。

通过探讨来加强对于MongoDB的认识

环境准备

技术选型

1.由于是验证性质的课题,这里没有使用MVC5/6.如果有人对MVC6有兴趣,可以另开一个课题讨论。这里使用的是传统的WebForm。

2.使用MongoDB最新版本作为数据库

3.MongoDB的GUI使用本人自己开发的工具

4.项目Host在阿里云上

截图1.

阿里云上的MongoDB数据库,使用本人开发工具看到的结果图

5.启用  meishiyouji.com 域名

6.UI使用的是  UIKit  这个库,进行响应式布局

7.使用官方的MongoDB的C#驱动程序

开发实践

1.MongoDB作为阶层型数据库的代表,其最大特点就是Free的存储方式,一个Collection(相当于数据表)里面可以存储各种结构的文档。

这个特性,如果用得好,非常有帮助,如果用的不好,容易造成混乱。例如我们可以将继承同一个基类的子类实例放在同一个Collection里面,

这个是传统数据库无法做的,不同的子类的字段数会不一样,无法放到同一个表里面去,但是MongoDB是可以做到的。

例子:

基类如下

例如一个行程的详细信息的基类如下:

 using Common.Database;
using Common.Misc;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver.Builders;
using System.Collections.Generic; namespace Common.Schedule
{
[BsonKnownTypes(typeof(Traffic), typeof(Visit), typeof(Shopping), typeof(Catering))]
public class DetailScheduleInfoBase : EntityBase
{
/// <summary>
/// 数据集名称
/// </summary>
public static string CollectionName = "DetailScheduleInfo";
/// <summary>
/// 序列号前缀
/// </summary>
public const string Prefix = "DI";
/// <summary>
///
/// </summary>
public static string NewSN = Prefix + .ToString("D8");
/// <summary>
/// OverviewSN
/// </summary>
public string OverviewSN = string.Empty;
/// <summary>
/// DiarySchduleSN
/// </summary>
public string DiarySchduleSN = string.Empty;
/// <summary>
/// 说明
/// </summary>
public string Description = string.Empty;
/// <summary>
/// Visit
/// </summary>
public string Location = string.Empty;
/// <summary>
/// 移动开始时间
/// </summary>
public string FromTime = string.Empty;
/// <summary>
/// 移动终止时间
/// </summary>
public string ToTime = string.Empty;
/// <summary>
/// 分类
/// </summary>
public SchduleTypeEnum SchduleType = SchduleTypeEnum.Other;
/// <summary>
/// 图片
/// </summary>
public string UploadImgUrl = string.Empty;
/// <summary>
/// 分类中文
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
public static string GetSchduleTypeEnumChinese(SchduleTypeEnum c)
{
string SchduleTypeEnumChinese = string.Empty;
switch (c)
{
case SchduleTypeEnum.Traffic:
SchduleTypeEnumChinese = "交通";
break;
case SchduleTypeEnum.Visit:
SchduleTypeEnumChinese = "参观";
break;
case SchduleTypeEnum.Shopping:
SchduleTypeEnumChinese = "购物";
break;
case SchduleTypeEnum.Catering:
SchduleTypeEnumChinese = "饮食";
break;
//case SchduleTypeEnum.Rest:
// SchduleTypeEnumChinese = "休息";
// break;
//case SchduleTypeEnum.Certificates:
// SchduleTypeEnumChinese = "手续";
// break;
case SchduleTypeEnum.Other:
SchduleTypeEnumChinese = "其他";
break;
default:
break;
}
return SchduleTypeEnumChinese;
}
/// <summary>
/// 分类枚举
/// </summary>
public enum SchduleTypeEnum
{
/// <summary>
/// 交通
/// </summary>
Traffic,
/// <summary>
/// 参观
/// </summary>
Visit,
/// <summary>
/// 饮食
/// </summary>
Catering,
/// <summary>
/// 购物
/// </summary>
Shopping,
///// <summary>
///// 休息
///// </summary>
//Rest,
///// <summary>
///// 手续
///// </summary>
//Certificates,
/// <summary>
/// 其他
/// </summary>
Other
}
/// <summary>
/// 备注
/// </summary>
public string Comments = string.Empty;
/// <summary>
/// 是否为一个备选方案
/// </summary>
public bool IsBack = false;
/// <summary>
/// 消费记录
/// </summary>
public List<Consumption.ConsumptionAbstract> ConsumptionList = new List<Consumption.ConsumptionAbstract>();
/// <summary>
/// 添加详细行程
/// </summary>
/// <param name="info"></param>
public static void Insert(DetailScheduleInfoBase info, string CreateUserName)
{
Operater.InsertRec(Prefix, CollectionName, info, CreateUserName);
}
/// <summary>
/// 通过序列号获得概要对象
/// </summary>
/// <param name="SN"></param>
public static DetailScheduleInfoBase GetDetailScheduleBySN(string DetailScheduleSN)
{
var query = Query.EQ("_id", DetailScheduleSN);
return Operater.GetFirstRec<DetailScheduleInfoBase>(CollectionName, query);
}
/// <summary>
/// 更新概要对象
/// </summary>
/// <param name="newobj"></param>
public static void Update(DetailScheduleInfoBase newobj, string UserName)
{
Operater.UpdateRec(CollectionName, newobj, UserName);
}
}
}

某一个子类的代码如下

 namespace Common.Schedule
{
public class Traffic : DetailScheduleInfoBase
{
public Traffic()
{
SchduleType = SchduleTypeEnum.Traffic;
}
/// <summary>
/// 移动开始地址
/// </summary>
public string MoveFromLocation = string.Empty;
/// <summary>
/// 移动终止地址
/// </summary>
public string MoveToLocation = string.Empty;
/// <summary>
/// 交通手段
/// </summary>
public TrafficTypeEnum TrafficType = TrafficTypeEnum.Walk;
/// <summary>
/// 交通手段枚举
/// </summary>
public enum TrafficTypeEnum
{
/// <summary>
/// 步行
/// </summary>
Walk,
/// <summary>
/// 出租
/// </summary>
Taxi,
/// <summary>
/// 巴士
/// </summary>
Bus,
/// <summary>
/// 捷运
/// </summary>
JieYun,
/// <summary>
/// 台铁
/// </summary>
Train,
/// <summary>
/// 高铁
/// </summary>
SpeedTrain,
/// <summary>
/// 飞机
/// </summary>
AirPlane,
/// <summary>
/// 轮船
/// </summary>
Ship
}
/// <summary>
/// 交通手段中文
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
public static string GetTrafficTypeEnumChinese(TrafficTypeEnum c)
{
string CurrencyEnumChinese = string.Empty;
switch (c)
{
case TrafficTypeEnum.Walk:
CurrencyEnumChinese = "步行";
break;
case TrafficTypeEnum.Taxi:
CurrencyEnumChinese = "出租";
break;
case TrafficTypeEnum.Bus:
CurrencyEnumChinese = "巴士";
break;
case TrafficTypeEnum.JieYun:
CurrencyEnumChinese = "捷运";
break;
case TrafficTypeEnum.Train:
CurrencyEnumChinese = "台铁";
break;
case TrafficTypeEnum.SpeedTrain:
CurrencyEnumChinese = "高铁";
break;
case TrafficTypeEnum.AirPlane:
CurrencyEnumChinese = "飞机";
break;
case TrafficTypeEnum.Ship:
CurrencyEnumChinese = "轮船";
break;
default:
break;
}
return CurrencyEnumChinese;
}
}
}

基类和子类可以存放在同一个数据集里面

1.注意 _t 字段,这里保存着基类的类型,如果是子类的话,这个字段是空的。

2.编码的时候请注意,子类和基类 保存/序列化 的时候不会出现什么问题,但是读取/反序列化的时候,一定会出现错误。

请在基类上增加这样的特性标签

  [BsonKnownTypes(typeof(Traffic), typeof(Visit), typeof(Shopping), typeof(Catering))]

这样的话,实际上是为系统注册了基类和子类关系,就可以正确的反序列化了。

我们是否要将一个巨大的对象保存为一个数据文档(数据文档在MongoDB里面是一条记录的意思)

一个巨大的对象,往往是一个树型结构的数据。

例如,一个旅行行程表,它包含了一个行程概要,一个礼物列表,一个[每日行程列表],一个事前准备列表。

每一个  [每日行程列表] 又包含了 【详细行程】列表,【详细行程】中又包含了【消费明细】等等。

这样的话,一个行程就是一个巨大的对象(巨大的树形结构数据),当然,MongoDB是支持将整棵树作为一个数据文档,一下子保存到数据库中去的。

(每一个数据文档的大小是有限制的,但是那个限制也是一个天文数字,我想没有人会把整部三国演义作为一个对象放到数据库里面去的吧)

不过,这样对于数据库的管理将是一个巨大的灾难,这个灾难不仅仅是在数据进行更新时候,更新命令将会变得很复杂,而且会在各种聚合操作的时候,将会遍历所有的数据对象。

这里使用了一种折衷的办法,将一个3层或者4层的结构,拆分为两个数据表,一个里面包含上面两层数据,然后做一个摘要结构,包含着下两层信息的一个简报。一些简单的信息直接从简版里面读取。

下两层则放置在另一个数据库中。只有在需要详细数据的时候才读取出来呈现在界面上。

这里的设计也就是一个数据存储的粒度的设计,粒度太小,就退化为关系型数据库。反之则每次更新将涉及整棵文档树。粒度设计是MongoDB的一个难点。

2.将MongoDB作为文件服务器使用

MongoDB的Grid File System特性就是一个内置的文件存储空间。你可以像管理数据一样管理文件。

对于文件的读取,其实很简单

C#驱动的Download和Upload方法提供了读取和保存文件的方法

         /// <summary>
/// 保存文件
/// </summary>
/// <param name="file"></param>
/// <param name="Username"></param>
/// <returns></returns>
public static string InsertFile(HttpPostedFile file, string Username)
{
string Mongofilename = Username + "_" + System.DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + file.FileName;
MongoGridFS gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
gfs.Upload(file.InputStream, Mongofilename);
return Mongofilename;
}
/// <summary>
/// 获得文件
/// </summary>
/// <param name="stream"></param>
/// <param name="filename"></param>
public static void GetFile(Stream stream,string filename)
{
MongoGridFS gfs = innerFileServer.GetGridFS(new MongoGridFSSettings());
gfs.Download(stream,filename);
}

当然,我们想做的事情是让IIS请求图片的时候,不走静态文件这条路,而是从数据库里面读取文件。

这里我们要自定义 http Handler

a.修改 webconfig

注意,经典模式和集成模式是不同的

我们让系统对于图片文件,用我们自定义的方法进行处理,JPG,GIF,PNG的响应使用ImageServer类来处理

  <system.webServer>
<handlers>
<add name="ImageServerJPG" path="*.jpg" verb="GET" type="TaiWanTripPlanSite.ImageServer" />
<add name="ImageServerGIF" path="*.gif" verb="GET" type="TaiWanTripPlanSite.ImageServer" />
<add name="ImageServerPNG" path="*.png" verb="GET" type="TaiWanTripPlanSite.ImageServer" />
</handlers>
</system.webServer>

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年前端经验的人应聘,我将会亲自带你们学习知识,提高技能,让你们实现自我价值。

使用MongoDB作为后台数据库的尝试的更多相关文章

  1. MongoDB和Redis-NoSQL数据库-文档型-内存型

    1NoSQL简述 CAP(Consistency,Availabiity,Partitiontolerance)理论告诉我们,一个分布式系统不可能满足一致性,可用性和分区容错性这三个需求,最多只能同时 ...

  2. MySQL、MongoDB、Redis数据库Docker镜像制作

    MySQL.MongoDB.Redis数据库Docker镜像制作 在多台主机上进行数据库部署时,如果使用传统的MySQL的交互式的安装方式将会重复很多遍.如果做成镜像,那么我们只需要make once ...

  3. 利用ajax的方式来提交数据到后台数据库及交互功能

    怎么样用ajax来提交数据到后台数据库,并完成交互呢????? 一.当我们在验证表单的时候,为了阻止把错误的也发送到服务器,我们通常这样设置:     $(function(){       var ...

  4. MongoDB非关系型数据库开发手册

    一:NoSql数据库 什么是NoSQL? NoSQL,指的是非关系型的数据库.NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称. NoSQL用于超 ...

  5. MySQL、MongoDB、Redis 数据库之间的区别

    NoSQL 的全称是 Not Only SQL,也可以理解非关系型的数据库,是一种新型的革命式的数据库设计方式,不过它不是为了取代传统的关系型数据库而被设计的,它们分别代表了不同的数据库设计思路. M ...

  6. 项目依赖模块解决、二次封装Response、后台数据库配置、user模块user表设计、前台创建及配置

    今日内容概要 二次封装Response 后台数据库配置 user模块user表设计 前台创建及配置 内容详细 补充--项目依赖模块 # 导出项目依赖模块和安装项目依赖模块 第三方模块--->导出 ...

  7. 第二章 存储,2.2 AliCloudDB--双11商家后台数据库的基石(作者:玄惭)

    2.2 AliCloudDB--双11商家后台数据库的基石 前言 2016年天猫双11购物狂欢节已经完美落下帷幕,千亿成交的背后,作为整个天猫商家后台数据库的基石,AliCloudDB是如何保障在零点 ...

  8. 【MongoDB】MongoDB VS SQL数据库

    MongoDB和SQL数据库都能满足数据库的基本功能:1.有组织的存放数据:2.按照需求查询数据 传统的SQL数据库(e.g.Oracle, MySQL) 对表的运用不够灵活,横向扩展不太容易,而它的 ...

  9. wordpress 如何从后台数据库修改theme(图文教程)

    我们在wordpress主题theme配置的时候,会从网站上下载比较流行的theme,使自己的blog看着很酷,也有不顺利的时候,你下载的theme有bug或者下载包出问题了,安装过后你的web页面不 ...

随机推荐

  1. 实现两个MySQL数据库之间的主从同步

    一.    概述MySQL从3.23.15版本以后提供数据库复制(replication)功能,利用该功能可以实现两个数据库同步.主从模式.互相备份模式的功能二.    环境操作系统:Linux 2. ...

  2. salesforce 零基础学习(二十四)解析csv格式内容

    salesforce中支持对csv格式的内容批量导入,可以使用dataloader,然而有些情况下,当用户没有相关权限使用dataloader导入情况下,就的需要使用VF和apex代码来搞定. 基本想 ...

  3. C#:使用Twain协议实现扫描仪连续扫描

    如果用Twain协议实现一次扫描多张图片?相信有接触过Twain协议与扫描仪通信的朋友都遇到过这样的问题.按照网上的例子,在连续送纸的扫描仪中,调用一次却只能扫描一张图片,怎么破? 关于这个问题 我研 ...

  4. JavaScript开发的技巧

    1. 使用===取代==    ==和!=操作符会在需要的情况下自动转换数据类型.但===和!==不会,它们会同时比较值和数据类型,这也使得它们要比==和!=快. "){ //速度慢 } & ...

  5. DELPHI支付宝支付代码

    真实业务场景的考虑 按照支付宝或者微信支付的开发手册的说法,一个标准的客户端接入支付业务模型应该是这样的,我忽略时序图,只用文字描述: 用户登录客户端,选择商品,然后点击客户端支付. 客户端收集商品信 ...

  6. python中常用的函数与库一

    1, collections.deque 在python里如果我们用列表作为队列使用也是可以的,只是当从队尾删除或者增加元素的时候是很快的,但是从队首删除或者增加元素则要慢得多,这是因为在队首进行操作 ...

  7. 【WP8.1开发】认识后台任务

    在手机上,使用后台,不像电脑上那么随意,准确地讲嘛,在移动平台上,后台任务都有严格的限制.至于说为什么会有这么多限制,我估计初衷很明显——保证系统的性能不受某个或某几个应用的负面影响:另外就是出于安全 ...

  8. Distribution of Data Through OCAF Tree

    Distribution of Data Through OCAF Tree OCAF数据的分配 eryar@163.com 一.作者注 Annotation 本文档主要用于说明OCAF(Open C ...

  9. Android入门(十一)SQLite CURD

    原文链接:http://www.orlion.ga/594/ 一.添加数据 SQLiteOpenHelper的getReadableDatabase()或getWritableDatabase()方法 ...

  10. 将数据转化成字符串时:用字符串的链接 还是 StringBuilder

    /* 目的:将数据转化成字符串时:用字符串的链接 还是 StringBuilder呢? */ public class Test{ public static void main(String[] a ...