zoukankan      html  css  js  c++  java
  • Mongodb官方驱动的进一步包装

        最近有一个需求,对数据的实时性要求比较高,之前寻找过一些内存数据库,首先将收费的产品先排除掉,然后再排除一些嵌入式产品,最终留下两个产品:
        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 == nullthrow new ArgumentNullException();
                
    return this.InsertEntities(entity,ESafeMode.False);
            }
            
    public EInsertstatus InsertOnSubmit(TEntity entity, ESafeMode safeMode)
            {
                
    if (entity == nullthrow new ArgumentNullException();
                
    return this.InsertEntities(entity, safeMode);
            }
            
    public void InsertBatchOnSubmit(List<TEntity> entity)
            {
                
    if (entity == nullthrow new ArgumentNullException();
                
    this.InsertBatchEntities(entity);
            }
            
    public bool UpdateOnSubmit(TEntity entity)
            {
                
    if (entity == nullthrow new ArgumentNullException();
                
    return this.UpdateEntites(entity);
            }
            
    public bool DeleteOnSubmit(TEntity entity)
            {
                
    if (entity == nullthrow new ArgumentNullException();

                
    return this.DeleteEntities(entity);
            }


            
         3:对查询的修改,在将实体映射为mongo文档时,需要对我们自定义的实体提供一个Map,这个Map主要是为了标识哪个是主键,哪些字段是可以被Linq识别的字段,比如我们要按学生ID查询,就需要在Map中增加此字段,示例如下:
            

    public class AggregateCompanyRecruitStudentInfoMap : EntityMap<AggregateCompanyRecruitStudentInfo>
        {
            
    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的一个简化结构图分享给大家:


           
           说明:最后欢迎大家交流mongodb,文中如有不对的地方,希望批评指正,大家共同进步。

  • 相关阅读:
    pg-xl 基于 pgxc_ctl 安装与使用
    pg-xl 的基本方式添加节点
    pg-xl 的基本方式安装与使用
    oracle 12.2.0.1 + oracle goldengate 12.3.0.1.4 之一 dml
    postgresql 高可用 etcd + patroni 之二 patroni
    postgresql 高可用 etcd + patroni 之一 etcd
    rvm,ruby的安装
    oracle 12.2.0.1 使用 active dataguard broker 之二 failover
    oracle 12.2.0.1 使用 active dataguard broker 之二 switchover
    es 6.3 .tar.gz 安装
  • 原文地址:https://www.cnblogs.com/ASPNET2008/p/2155280.html
Copyright © 2011-2022 走看看