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

[经验分享] Mongodb官方驱动的进一步包装

[复制链接]

尚未签到

发表于 2015-7-5 14:00:31 | 显示全部楼层 |阅读模式
  
  最近有一个需求,对数据的实时性要求比较高,之前寻找过一些内存数据库,首先将收费的产品先排除掉,然后再排除一些嵌入式产品,最终留下两个产品:
    1:Mysql内存引擎;
    2:基于内存文件映射的文档数据库Mongodb;
  
    针对以上两种产品,mysql内存引擎有如下缺点是我们放弃的理由:
    1:数据量比较大的情况,比之innodb引擎优势并不明显,数据小的时候采用B-Tree索引结构性能还是可以接受的。
    2:内存引擎对于持久化是非常吃力的,如果大量数据操作均在内存引擎中完成,那么就需要我们来做数据的持久化,无论是复制订阅方式还是程序方式均存在比较大的困难。
  3:内存引擎所占用的数据空间比较大,特别是默认的hash结构,同数据量的数据是sql server的6倍,B-Tree结构未做具体比较。
  
    选择Mongodb的理由:
    1:自身具备数据的持久化;
    2:对于数据的插入性能优越,特别是采用不返回值的模式,理论上来讲是一种异步操作;
    3:由于文档存储是k-value方式,所以对于单条记录的查询操作性能不用考虑;
  4:对于水平扩展,数据的主从备份均非常容易。
  
    使用Mongodb过程中遇到过的问题:
    1:由于存储的内容完全由客户端决定,于是对于一些对字段做更新的操作,比如sql 中的set count=count+1;存在一个并发更新问题,当两个线程同时需要对count字段加1时,两个线程同时查询到的count内容为1,当线程A完成加1后,数据库中实际已经变成2,此时线程B对count进行加1,此时也是2,于时原来是3的内容,最终会变成2。主要原因就在于更新字段时并不会在服务端进行,字段的内容完全是由客户端决定,即更新时并不会像sql server一样,在服务端变量基础上再做操作。
  
    解决方案:对记录增加自定义的锁标识,尝试在记录上增加锁,如果加锁成功,然后再查询,如果锁标识证明是当前线程才进行更新,否则循环尝试加锁。这里有发生死锁的可能,为此在加锁标记时,可以增加一个锁时间,当超过锁时间后,自动解锁,这样可以避免死锁。
  
    2:数据的插入,主要有两种模式,一种是不返回处理结果,一种是返回处理结果,如果我们不太关心返回结果,那么可以获得最高插入性能,对于一些关注处理结果的业务场景,就需要采取返回结果方式。
  
    3:最好自定义主键,不采用默认的_id值,我们处理一条学生记录,学生记录是唯一的,比如学生ID,如果我们采用默认_id值,那么在插入数据时也会存在并发问题:两个线程同时处理学生A,当时查询时发现数据库中没有学生A的记录,于时就进行插入,由于默认的_id值是自动生成,类似guid或者是自增id,说的通俗点就是会生成一个不重复的key,此时就会插入学生A记录两次,如果我们选用学生ID做为主键,就可以避免此种情况,当第二次尝试插入时,系统会抛出主键重复的异常。
   
    为什么需要对mongodb驱动重新封装?
    其实无论是samus驱动还是官方驱动,其实功能都各有秋千,samus驱动对数据操作进行了Linq封装,即我们在操作List时,完全可以采用类似Linq一样的语法,这样可以使我们的学习成本降低,官方驱动的特点是针对数据处理有返回值。我们的需求是即需要操作返回值也需要Linq封装,于时我找到了老赵写的easyMongo,但在它的基本上做了一小部分取舍,取舍内容如下:
   
    1:去掉了如下逻辑,原意是将大多数操作均放在同一连接中处理,但不满足我们的需求;我们直接对外提供一次性执行方法
  


InsertOnSubmit,UpdateOnSubmit,DeleteOnSubmit
     public void SubmitChanges()
        {
            using (this.Database.Server.RequestStart(this.Database))
            {
                this.DeleteEntities();
                this.UpdateEntites();
                this.InsertEntities();
            }
        }  
  2:对上面所提到的insert,update ,delete要求有返回值,对于insert提供两种模式,一类是需要返回值一类则不需要。特别是像更新和删除,我们是非常有必要知道它的处理结果,如果不关心结果,我认为可以是一些数据准确性要求不高的场景。
  


     public EInsertstatus InsertOnSubmit(TEntity entity)
        {
            if (entity == null) throw new ArgumentNullException();
            return this.InsertEntities(entity,ESafeMode.False);
        }
        public EInsertstatus InsertOnSubmit(TEntity entity, ESafeMode safeMode)
        {
            if (entity == null) throw new ArgumentNullException();
            return this.InsertEntities(entity, safeMode);
        }
        public void InsertBatchOnSubmit(List entity)
        {
            if (entity == null) throw new ArgumentNullException();
            this.InsertBatchEntities(entity);
        }
        public bool UpdateOnSubmit(TEntity entity)
        {
            if (entity == null) throw new ArgumentNullException();
            return this.UpdateEntites(entity);
        }
        public bool DeleteOnSubmit(TEntity entity)
        {
            if (entity == null) throw new ArgumentNullException();
            return this.DeleteEntities(entity);
        }  
        
     3:对查询的修改,在将实体映射为mongo文档时,需要对我们自定义的实体提供一个Map,这个Map主要是为了标识哪个是主键,哪些字段是可以被Linq识别的字段,比如我们要按学生ID查询,就需要在Map中增加此字段,示例如下:
        


public class AggregateCompanyRecruitStudentInfoMap : EntityMap
    {
        public AggregateCompanyRecruitStudentInfoMap()
        {
            Collection("AggregateCompanyRecruitStudentInfo");
            Property(n => n.OrganizationID).Identity();
            Property(n => n.LockTime);
            Property(n => n.LockFiled);
        }
    }  
  但源码中的查询,如果没有提供查询表达式,则默认只返回Map中提到的字段,对其它实体字段则不处理,为此修改如下:即没有查询表达式的情况返回完整文档。
      


FieldsDocument fieldsDoc = null;
            if (null != selector)
            {
                fieldsDoc = mapper.GetFields(selector);
            }  
  4:去掉原有复杂的更新逻辑,删除对我们没用的状态跟踪,要的就是简单直接,过于复杂的逻辑不利于开发。
      
    Mymongo的一个简化结构图分享给大家:
DSC0000.png
      
       说明:最后欢迎大家交流mongodb,文中如有不对的地方,希望批评指正,大家共同进步。
  

运维网声明 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-83399-1-1.html 上篇帖子: MongoDb企业应用实战(一) 写在MongoDb应用介绍之前(i) 下篇帖子: MongoDB如何存储数据
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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