Amint azt már valószínűleg tudja, a MongoDB már gyors olvasási lekérdezések végrehajtása során, és különösen akkor, ha indexeket használ

Ha nem ismeri a MongoDB indexeit, itt van egy gyors összefoglaló;

A MongoDB rendelkezik valamivel, amit belsőleg indexnek neveznek, az indexet egy egyedi gyűjteményhez illesztik.

Ez az index egy hatékony adatstruktúra az adott gyűjteményben található rekordkészletek megkereséséhez.
Tehát ahelyett, hogy egy rekordot kellene megnézni, és megnézni, hogy az megegyezik-e a lekérdezéssel és az azt követővel. így a (A teljes gyűjtemény vizsgálata) esetén az index lehetővé teszi, hogy közvetlenül a számunkra fontos rekordhoz ugorjunk.

Azonban nagyon könnyen írhat olyan lekérdezéseket, amelyek nem egyeznek meg egy indexszel, vagy amelyekhez nem áll rendelkezésre index.
Tehát ezekben a helyzetekben nagyon könnyen komoly teljesítményproblémákba ütközhet az alkalmazása körül.

Kétféleképpen lehet megoldani ezt a teljesítményproblémát:

1- Több index létrehozása minden gyűjteményhez:

Ez valóban nyilvánvaló dolognak tűnhet.

Ha azonban indexeket ad hozzá egy gyűjteményhez, az hatással van arra, hogy képes-e hatékonyan írni a gyűjteménybe.

Ezenkívül előfordulhat, hogy egy alkalmazáson belül végez lekérdezéseket, ahol nem igazán tudja előre kitalálni, hogy milyen indexekre van szüksége.

2- Adatok gyorsítótárazása a Redis segítségével

A Redis egy nyílt forráskódú, memórián belüli adatszerkezet-tár, amelyet adatbázisként, gyorsítótárként és üzenetközvetítőként használnak.

Íme egy illusztráció a Redis gyorsítótár-kiszolgálóként való megvalósítására:

Minden alkalommal, amikor a mongoose lekérdezést ad ki, először a Redishez fog eljutni, a Redis ellenőrizni fogja, hogy ezt a lekérdezést kiadták-e már korábban.

Ha nem, akkor a szerver átveszi a lekérdezést, és elküldi a MongoDB-nek, a Mongo pedig végrehajtja a lekérdezést.

Ezután veszi a lekérdezés eredményeit, és tárolja a Redisben

Mostantól minden alkalommal, amikor ugyanazt a pontos lekérdezést adják ki, a Mongoose ugyanazt a lekérdezést küldi át a Redisnek, de ezúttal, ha a Redis azt látja, hogy a lekérdezést már egyszer elküldték, mielőtt a lekérdezést elküldi a MongoDB-nek.

Ehelyett azt a választ veszi a lekérdezésre, amelyet utoljára kapott, és azonnal visszaküldi a mongoose-nak.

Néhány megjegyzés, mielőtt elkezdjük:

A Redis egy adattár, amely csak a memóriában működik, tehát ha kikapcsolják, újraindítják vagy bármi hasonlót, az összes benne lévő adat azonnal törlődik és törlődik.

Tehát a gyakorlatban csak olyan adatokra használjuk a Redis-t, amelyekről úgy érezzük, rendben van, miután hirtelen eltűntek a levegőben, mert ki tudja, lehet, hogy elveszítjük az áramot, vagy újraindul a gépünk.

Csak számokat és betűket tudunk tárolni a Redisben, ezért sok JSON.stringify() és JSON .parse() a kódban.

Az Express.js-t és a Mongoose-t fogjuk használni a MongoDB és a Redis rendszerrel

MOST KEZDJÜK:

ELSŐ LÉPÉS:

  • Telepítse a Redis-t a gépére, és ne felejtse el hozzáadni aPATH KÖRNYEZETI VÁLTOZÓIHOZ (Ezt mind a google-ban megtalálhatja)

KEZDJÜK A KÓDOLÁST:

Az összes kódot megtalálja ebben aGithub repoban,bontsa ki/klónozza, és kövesse.

Mielőtt továbbmennénk, beszéljük meg, hogyan fogjuk megvalósítani ezt a gyorsítótárazást

Tudom, hogy ez valahogy őrülten fog hangzani, de nagyon jól fog sikerülni.

Módosítjuk, hogy a mongoose hogyan készíti el és hajtja végre a lekérdezést

Gondoljunk csak bele, az egész gyorsítótárazási stratégiánk azon az elképzelésen alapul, hogy valahogy megakadályozzuk, hogy a Mongoose lekérdezést hajtson végre a Mongo felé.

És azon az ötleten is alapul, hogy valahogy elfogjuk a Mongóból visszaérkező értéket is, hogy a készpénzünkben tárolhassuk.

Tehát ez az egész gyorsítótárazási ötlet hihetetlenül szorosan párosul a mangúzzal.

Most a Szolgáltatások mappában létrehozunk egy cache.js nevű fájlt, ahol a gyorsítótárazás minden logikájával rendelkezünk.

Mindig néhány importtal kezdje: (Ne felejtse el npm-en telepíteni ezeket)

const mongoose = require('mongoose');
const redis = require('redis');
const util = require('util');

Így hozunk létre egy újradis-klienst:

const redis_url = 'redis://127.0.0.1:6379'
const client = redis.createClient(redis_url);

A redis_url esetén ez az alapértelmezett Redis.

Itt csak az util.promisify-t használjuk, hogy a „client.hget” függvény ígéretet adjon visszahívás elfogadása helyett (csak kerülje a visszahívások írását minden alkalommal, amikor ezt a funkciót hívjuk)

client.hget = util.promisify(client.hget);

A hget függvény a beágyazott hashek lekérésére szolgáló függvény a Redisből.
A hset függvény pedig a Redis beágyazott hash-ek tárolására szolgáló függvény.

Mint láthatja, minden alkalommal visszahívást kell használnunk, amikor hget-et hívunk, és ez nagyon fárasztó.

hget('spanish', 'red', (err, val) => console.log(val))

Az util.promisify használata után a következőképpen nevezhetjük:

const cacheValue = await client.hget('spanish', 'red')

Most a következő lépés a mongoose folyamat eltérítése, és a monkey patch mongoose exec függvénye, hogy ellenőrizze a Redis belsejében, mielőtt lekérdezéseket küldene a Mongo-nak:

előtte meg kell javítanunk a cache : nevű új módszert

mongoose.Query.prototype.cache = function (options = {}) {
    this.useCache = true;
    this.hashKey = JSON.stringify(options.key || '');
    return this;
}

Ezzel a funkcióval küldjük el a Kulcsot (az első argumentum a hset függvényben), és kiválasztjuk, hogy a lekérdezést a Redis tárolja-e vagy sem.
Ennek használata a függvény valami ilyesmi lesz:

const blogs = await Blog.find({ _user: req.user.id })
    .cache({
      key: req.user.id
    });

Most térjünk át az exec függvényre:

Először elmentjük

const exec = mongoose.Query.prototype.exec;

Most majom javítjuk:

mongoose.Query.prototype.exec = async function () {
    if (!this.useCache) {
        return exec.apply(this, arguments)
    }

    const key = JSON.stringify(Object.assign({}, this.getQuery(), {
        collection: this.mongooseCollection.name
    }));

    const cacheValue = await client.hget(this.hashKey, key)

    if(cacheValue) {
        const doc = JSON.parse(cacheValue);

        return Array.isArray(doc)
            ? doc.map(d => new this.model(d))
            : new this.model(doc);
    }

    const result = await exec.apply(this, arguments);

    client.hset(this.hashKey, key, JSON.stringify(result));
    return result
}
module.exports = {
    clearHash(hashKey) {
        client.del(JSON.stringify(hashKey));
    }
}

Szépítsük ezt apró darabokra:

Itt ellenőrizzük, hogy meghívtuk-e a „cache” metódust, ha nem, akkor csak hagyja, hogy a mongoose zavartalanul végezze a szokásos munkáját, különben elindítjuk a gyorsítótárazási folyamatot.

if (!this.useCache) {
        console.log("FETCHING FROM MONGO")
        return exec.apply(this, arguments)
    }

Itt tároljuk a lekérdezést és a lekérdezést végrehajtó gyűjtemény nevét egy kulcs nevű objektumban (ez lesz a HSET függvény második és harmadik argumentuma)

const key = JSON.stringify(Object.assign({}, this.getQuery(), {
    collection: this.mongooseCollection.name
}));

Itt megpróbáljuk lekérni az adatokat a Redisből a hget használatával

const cacheValue = await client.hget(this.hashKey, key)

if(cacheValue) {
    const doc = JSON.parse(cacheValue);

    return Array.isArray(doc)
        ? doc.map(d => new this.model(d))
        : new this.model(doc);
}

Ha megtaláljuk a lekérdezés pontos egyezését, akkor egy sima javascript objektumból Mongoose Documents-re alakítjuk át, és visszaadjuk az eredményt

Ellenkező esetben adja ki a kívánt lekérdezést Mongónak, és tárolja az eredményt a Redisben:

const result = await exec.apply(this, arguments);

client.hset(this.hashKey, key, JSON.stringify(result));
return result

Így tároljuk és kérjük le az adatokat a Redisből

DE MINDIG TÖRÍTNÜNK KELL A REDIS GYORSÍTÓTEREMÉT, AMIKOR BÁRMILYEN VÁLTOZÁST VÉGEZÜNK, EGYÉBEN MINDIG UGYANAZON ADATOKAT KERESÜNK BE A REDIS-RŐL

Ez a clearHash az a függvény, amely elnémítja az adott hashKey-hez kapcsolódó összes adatot (ugyanaz az első argumentum, amelyet a hset/hget-ben használtunk)

module.exports = {
    clearHash(hashKey) {
        client.del(JSON.stringify(hashKey));
    }
}

Most a middlewares mappában létrehozunk egy fájlt clearCache.js néven.

const { clearHash } = require('../services/cache')

module.exports = async (req, res, next) => {
    await next();

    clearHash(req.body.userId);

}

Ezt a funkciót köztes szoftverként használjuk a posta útvonalunkon vagy bármely más olyan útvonalon, amely megváltoztatja az adatainkat, és frissíteni kell

Valahogy így fog kinézni:

router.post('/', clearCache, async (req, res) => {
    const { userId, title, content } = req.body

    const user = await User.findById(userId)

    const article = new Article({
        title,
        content,
        user
    })
    await article.save();
    res.json(article);

})

A clearHash függvény meghívja a next()-et, ami az általunk beállított útvonalkezelő, majd a végrehajtás befejezése után a clearHash folytatja a munkát és törli a szükséges adatokat!

Ezt a megoldást azért hozzuk létre, hogy a clearCache működjön, miután az útvonalkezelő a várt módon lefutott, így nem töröljük az adatokat a redisből, ha az útvonalkezelő nem végezte el a feladatát.

Ouuf ! BEFEJEZTÜK !

Mindenképpen nézze meg a kódot a github repo-ban.

A „github repo” alkalmazás teljesen működőképes, és tesztelheti vele a gyorsítótárazást, ha megfelelően telepítette az újratelepítést a gépére.

Eléggé egyértelmű, és elég gyorsan megértheti, ha van tapasztalata az expressz szolgáltatással kapcsolatban!

Lépjen kapcsolatba velem a Linkedin-en, és találkozunk a következő cikkben!