ObjectRepository - .NET patrún stór in-chuimhne do do thionscadail tí

Cén fáth a stóráil na sonraí go léir i gcuimhne?

Chun sonraí suímh ghréasáin nó inneall a stóráil, is é an chéad mhian a bheidh ag an gcuid is mó de na daoine ciallmhara bunachar sonraí SQL a roghnú. 

Ach uaireanta tagann an smaoineamh chun cuimhne nach bhfuil an tsamhail sonraí oiriúnach do SQL: mar shampla, nuair a thógáil cuardaigh nó graf sóisialta, ní mór duit a chuardach le haghaidh caidreamh casta idir rudaí. 

Is é an cás is measa ná nuair a oibríonn tú i bhfoireann agus nach bhfuil a fhios ag comhghleacaí conas fiosruithe tapa a thógáil. Cé mhéad ama a chaith tú ag réiteach fadhbanna N+1 agus ag tógáil innéacsanna breise ionas go gcríochnódh an SELECT ar an bpríomhleathanach laistigh de thréimhse réasúnta ama?

Cur chuige coitianta eile is ea NoSQL. Roinnt blianta ó shin bhí go leor hype thart ar an ábhar seo - d'aon ócáid ​​áisiúil d'imscar siad MongoDB agus bhí siad sásta leis na freagraí i bhfoirm doiciméad json (dála an scéil, cé mhéad crutches a bhí ort a chur isteach mar gheall ar na naisc ciorclach sna doiciméid?).

Molaim modh malartach eile a thriail - cén fáth nach ndéanann tú iarracht na sonraí go léir a stóráil i gcuimhne an fheidhmchláir, agus iad a shábháil go tráthrialta chuig stóráil randamach (comhad, cianbhunachar sonraí)? 

Tá cuimhne éirithe saor, agus beidh aon sonraí féideartha don chuid is mó de thionscadail bheaga agus mheánmhéide oiriúnach do 1 GB de chuimhne. (Mar shampla, is é an tionscadal baile is fearr liom lorgaire airgeadais, a choimeádann staitisticí laethúla agus stair mo chostais, iarmhéideanna, agus idirbhearta ar feadh bliana go leith, ídíonn ach 45 MB de chuimhne.)

Son:

  • Éiríonn rochtain ar shonraí níos éasca - ní gá duit a bheith buartha faoi cheisteanna, luchtú leisciúil, gnéithe ORM, oibríonn tú le gnáthrudaí C#;
  • Níl aon fhadhbanna a bhaineann le rochtain ó snáitheanna éagsúla;
  • An-tapa - gan aon iarratais líonra, gan aon aistriúchán ar an gcód go teanga iarratais, gan aon ghá le (dí)sraithiú réad;
  • Tá sé inghlactha sonraí a stóráil i bhfoirm ar bith - bíodh sé i XML ar dhiosca, nó i SQL Server, nó i Azure Table Storage.

CONS:

  • Cailltear scálú cothrománach, agus mar thoradh air sin, ní féidir imscaradh aga neamhfhónaimh nialasach a dhéanamh;
  • Má thiteann an feidhmchlár, seans go gcaillfidh tú cuid de na sonraí. (Ach ní thuairteanna ár n-iarratas, ceart?)

Conas a oibríonn sé?

Seo a leanas an algartam:

  • Ag an tús, bunaítear nasc leis an stóráil sonraí, agus tá na sonraí á luchtú;
  • Tógtar samhail oibiachta, innéacsanna príomhúla, agus innéacsanna coibhneasta (1:1, 1: go leor);
  • Cruthaítear síntiús le haghaidh athruithe ar airíonna oibiachta (INotifyPropertyChanged) agus chun eilimintí a chur leis nó a bhaint den bhailiúchán (INotifyCollectionChanged);
  • Nuair a spreagtar an síntiús, cuirtear an réad athraithe leis an scuaine chun scríobh chuig an stóráil sonraí;
  • Sábháiltear athruithe ar an stóráil go tréimhsiúil (ar an lasc ama) i snáithe cúlra;
  • Nuair a fhágann tú an feidhmchlár, sábhálfar athruithe ar an stóras freisin.

Sampla Cód

Ag cur na spleáchais is gá

// Основная библиотека
Install-Package OutCode.EscapeTeams.ObjectRepository
    
// Хранилище данных, в котором будут сохраняться изменения
// Используйте то, которым будете пользоваться.
Install-Package OutCode.EscapeTeams.ObjectRepository.File
Install-Package OutCode.EscapeTeams.ObjectRepository.LiteDb
Install-Package OutCode.EscapeTeams.ObjectRepository.AzureTableStorage
    
// Опционально - если нужно хранить модель данных для Hangfire
// Install-Package OutCode.EscapeTeams.ObjectRepository.Hangfire

Déanaimid cur síos ar an tsamhail sonraí a stórálfar sa stóráil

public class ParentEntity : BaseEntity
{
    public ParentEntity(Guid id) => Id = id;
}
    
public class ChildEntity : BaseEntity
{
    public ChildEntity(Guid id) => Id = id;
    public Guid ParentId { get; set; }
    public string Value { get; set; }
}

Ansin múnla an oibiachta:

public class ParentModel : ModelBase
{
    public ParentModel(ParentEntity entity)
    {
        Entity = entity;
    }
    
    public ParentModel()
    {
        Entity = new ParentEntity(Guid.NewGuid());
    }
    
    public Guid? NullableId => null;
    
    // Пример связи 1:Many
    public IEnumerable<ChildModel> Children => Multiple<ChildModel>(x => x.ParentId);
    
    protected override BaseEntity Entity { get; }
}
    
public class ChildModel : ModelBase
{
    private ChildEntity _childEntity;
    
    public ChildModel(ChildEntity entity)
    {
        _childEntity = entity;
    }
    
    public ChildModel() 
    {
        _childEntity = new ChildEntity(Guid.NewGuid());
    }
    
    public Guid ParentId
    {
        get => _childEntity.ParentId;
        set => UpdateProperty(() => _childEntity.ParentId, value);
    }
    
    public string Value
    {
        get => _childEntity.Value;
        set => UpdateProperty(() => _childEntity.Value, value
    }
    
    // Доступ с поиском по индексу
    public ParentModel Parent => Single<ParentModel>(ParentId);
    
    protected override BaseEntity Entity => _childEntity;
}

Agus ar deireadh, an rang stórtha féin chun rochtain a fháil ar shonraí:

public class MyObjectRepository : ObjectRepositoryBase
{
    public MyObjectRepository(IStorage storage) : base(storage, NullLogger.Instance)
    {
        IsReadOnly = true; // Для тестов, позволяет не сохранять изменения в базу
    
        AddType((ParentEntity x) => new ParentModel(x));
        AddType((ChildEntity x) => new ChildModel(x));
    
        // Если используется Hangfire и необходимо хранить модель данных для Hangfire в ObjectRepository
        // this.RegisterHangfireScheme(); 
    
        Initialize();
    }
}

Cruthaigh sampla ObjectRepository:

var memory = new MemoryStream();
var db = new LiteDatabase(memory);
var dbStorage = new LiteDbStorage(db);
    
var repository = new MyObjectRepository(dbStorage);
await repository.WaitForInitialize();

Má tá an tionscadal a úsáid HangFire

public void ConfigureServices(IServiceCollection services, ObjectRepository objectRepository)
{
    services.AddHangfire(s => s.UseHangfireStorage(objectRepository));
}

réad nua á chur isteach:

var newParent = new ParentModel()
repository.Add(newParent);

Leis an nglao seo, an réad Múnla Tuismitheora cuirtear leis an taisce áitiúil agus leis an scuaine chun scríobh chuig an mbunachar sonraí. Mar sin, glacann an oibríocht seo O(1), agus is féidir oibriú leis an réad seo láithreach.

Mar shampla, chun an réad seo a aimsiú sa stór agus a fhíorú gurb ionann an t-ábhar a cuireadh ar ais:

var parents = repository.Set<ParentModel>();
var myParent = parents.Find(newParent.Id);
Assert.IsTrue(ReferenceEquals(myParent, newParent));

Ceard ata ag tarlu? Socraigh () filleann TáblaFoclóir, ina bhfuil ConcurrentDictionary agus soláthraíonn sé feidhmiúlacht bhreise na n-innéacsanna bunscoile agus tánaisteacha. Ligeann sé seo duit modhanna a bheith agat chun cuardach a dhéanamh de réir Id (nó innéacsanna úsáideoirí treallach eile) gan atriallú iomlán a dhéanamh ar gach réad.

Nuair a chuirtear rudaí le ObjectRepository cuirtear síntiús leis chun a n-airíonna a athrú, mar sin de thoradh aon athrú ar airíonna cuirtear an réad seo leis an scuaine scríobh freisin. 
Tá an chuma chéanna ar réadmhaoin a nuashonrú ón taobh amuigh agus a bheith ag obair le réad POCO:

myParent.Children.First().Property = "Updated value";

Is féidir leat réad a scriosadh ar na bealaí seo a leanas:

repository.Remove(myParent);
repository.RemoveRange(otherParents);
repository.Remove<ParentModel>(x => !x.Children.Any());

Cuireann sé seo an réad leis an scuaine scriosta freisin.

Conas a oibríonn coigilt?

ObjectRepository nuair a athraíonn rudaí monatóireachta (ceachtar acu ag cur leis nó ag scriosadh, nó ag athrú airíonna), ardaítear imeacht Múnla Athraithesuibscríofa le Stóráil. Feidhmithe Stóráil nuair a tharlaíonn imeacht Múnla Athraithe cuirtear na hathruithe i 3 scuaine - le cur leis, le nuashonrú agus le scriosadh.

Chomh maith leis sin implementations Stóráil nuair a thosaíonn siad, cruthaíonn siad lasc ama a fhágann go sábhálfar athruithe gach 5 soicind. 

Ina theannta sin, tá API ann chun glaoch a shábháil: ObjectRepository.Sábháil().

Roimh gach sábháil, baintear oibríochtaí gan bhrí ar dtús as na scuainí (mar shampla, teagmhais dhúblacha - nuair a athraíodh réad faoi dhó nó nuair a cuireadh rudaí leis/bainte go tapa), agus gan ach ansin an sábháil féin. 

I ngach cás, déantar an réad reatha iomlán a shábháil, agus mar sin is féidir go sábhálfar réada in ord difriúil ná mar a athraíodh iad, lena n-áirítear leaganacha níos nuaí de réada ná mar a bhí ag an am a cuireadh leis an scuaine iad.

Cad eile atá ann?

  • Tá gach leabharlann bunaithe ar .NET Standard 2.0. Is féidir é a úsáid in aon tionscadal nua-aimseartha. GLAN.
  • Tá an API snáithe sábháilte. Cuirtear bailiúcháin inmheánacha i bhfeidhm bunaithe ar ConcurrentDictionary, bíonn glais ag láimhseálaithe imeachtaí nó ní bhíonn gá acu leo. 
    Is é an t-aon rud is fiú cuimhneamh ná glaoch ObjectRepository.Sábháil();
  • Innéacsanna treallach (gá uathúlacht):

repository.Set<ChildModel>().AddIndex(x => x.Value);
repository.Set<ChildModel>().Find(x => x.Value, "myValue");

Cé a úsáideann é?

Go pearsanta, thosaigh mé ag baint úsáide as an gcur chuige seo i ngach tionscadal caitheamh aimsire toisc go bhfuil sé áisiúil agus nach gá costais mhóra chun ciseal rochtana sonraí a scríobh nó bonneagar trom a imscaradh. Go pearsanta, is leor domsa sonraí a stóráil i liteb nó i gcomhad de ghnáth. 

Ach san am atá caite, nuair a bhí an tosaithe atá imithe as feidhm anois, EscapeTeams (Shíl mé anseo é, airgead - ach níl, taithí arís) - a úsáidtear chun sonraí a stóráil i Azure Table Storage.

Pleananna don todhchaí

Ba mhaith liom ceann de na príomh-mhíbhuntáistí a bhaineann leis an gcur chuige seo a shocrú - scálú cothrománach. Chun seo a dhéanamh, ní mór duit idirbhearta dáileacháin (sic!), nó cinneadh láidir a dhéanamh nach n-athródh na sonraí céanna ó chásanna éagsúla, nó ligean dóibh athrú de réir an phrionsabail “cé hé atá i gceart.”

Ó thaobh teicniúil de, feicim an scéim seo a leanas agus is féidir:

  • Stóráil EventLog agus Snapshot in ionad samhail oibiachta
  • Faigh cásanna eile (cuir críochphointí gach cás leis na socruithe? fionnachtain udp? máistir/sclábhaí?)
  • Déan macasamhlú idir cásanna EventLog trí aon algartam comhaontaithe, mar RAFT.

Tá fadhb eile ann freisin a chuireann imní orm - scriosadh cascáide, nó cásanna ina ndéantar rudaí a scriosadh a bhfuil naisc acu ó réada eile a bhrath. 

Cód foinseach

Má tá an bealach ar fad léite agat anseo, níl fágtha ach an cód a léamh; is féidir é a fháil ar GitHub:
https://github.com/DiverOfDark/ObjectRepository

Foinse: will.com

Add a comment