Table of Contents
Upotetut järjestelmät vaativat usein paikallista tiedonhallintaa ilman kaikkien tietokantapalvelimien yläpuolella. C-tietokannan kevyt moottori antaa kehittäjille suoran hallinnan muistin, suorituskyvyn ja tallennusjärjestelmän suhteen. Tämä artikkeli kulkee yksinkertaisen upotetun tietokantamoottorin suunnittelun ja toteuttamisen läpi, joka kattaa tietorakenteet, CRUD-toiminnot, indeksoinnin ja pysyvyysstrategiat resurssirajoitteisille ympäristöille.
Upotetun tietokantamoottorin perusvaatimukset
Sulautetun tietokantamoottorin on toimittava tiukassa RAM-, salama- ja käsittelynopeudella. Tyypillisiä vaatimuksia ovat deterministinen käyttäytyminen, minimaalinen koodijalanjälki, eikä ulkoisia riippuvuuksia. Moottorin pitäisi tukea perustoimintoja: lisätä, hakea, päivittää, poistaa ja etsiä. Monissa upotetuissa tietokannoissa on myös säilytettävä virranhukkaa ja tallennettava tietoja haihtumattomasta muistista, kuten EEPROMista, SPI-flashista tai SD-korteista.
Oikean datarakenteen valinta on ensimmäinen suunnittelupäätös. Array-mallit ovat yksinkertaisia mutta staattisia kokoja rajoittavia. Linkit mahdollistavat dynaamisen kasvun, mutta lisäävät osoitinta yläpuolella. Tasapainoisen suorituskyvyn saavuttamiseksi hybridimalli, jossa käytetään kiinteää-kokoinen levypooleja, joissa on vapaa lista, voi toimia hyvin. [SQLite:n suunnitteluperiaatteet [ tarjoavat hyödyllisiä oivalluksia jopa paljon yksinkertaisempiin moottoreihin.
Tallennuskerroksen suunnittelu
Tallennuskerros hallinnoi sitä, miten tietueet on asetettu muistiin tai levylle. Yleinen kuvio on käsitellä jokaista tietuetta kiinteänä rakenteena, joka yksinkertaistaa osoitinten aritmeettista laskentaa ja mahdollistaa suoran indeksoinnin. Muuttujanpituiset tietueet monimutkaistavat pirstoutumista ja vaativat muistinhallintaa.
Kiinteän ja pitkän aikavälin ennätykset ennätysallalla
Määrittele tietueiden enimmäismäärä (esim. ]) ja jaa staattinen matriisi. Käytetty bittikartta tai vapaa lista kappaleita, joita käytetään lähtö- ja saapumisaikojen poistossa. Kun tietue poistetaan, sen lähtö palaa pooliin. Tämä lähestymistapa välttää dynaamisen jaon ja takaa O(1) jakoajan.
#define MAX_RECORDS 256
typedef struct {
int id;
char name[32];
float value;
int active; // 1 if slot in use
} Record;
Record pool[MAX_RECORDS];
Jatkuvaa varastointia varten allas voidaan liittää tiedostoon tai salaman alueelle. Käynnistyksen aikana moottori lukee allasta haihtumattomasta muistista RAM-muistiin ja se kirjoittaa sen takaisin sammutuksen yhteydessä (tai ajoittain).
Tietojen eheystarkastukset
Lisää jokainen tietueeseen yksinkertainen tarkistussummakenttä korruption havaitsemiseksi. [CRC-32 on hyvä valinta sulautettuihin järjestelmiin, jolloin monimutkaisuus ja virheen havaitsemisen voimakkuus tasapainotetaan.
PerusCRUD-toimintojen toteuttaminen
Kun tietuepooli on määritelty, toteuttaa toimintoja lisätä, löytää, päivittää ja poistaa tietueita. Hakutoiminnot ovat usein suorituskyvyn pullonkaula, joten naiivi lineaarinen haku on hyväksyttävä vain pienille tietokannoille ( muutama sata tietuetta).
Lisää vapaa- luettelon hallinta
Säilytä vapaa lista indeksejä. Lisää, pop indeksin vapaa lista, täytä tietue, ja merkitse se aktiivinen. Vapaa lista itse voi olla yksinkertainen pino käyttäen joukko kokonaislukuja.
int free_list[MAX_RECORDS];
int free_count = MAX_RECORDS;
for (int i = 0; i < MAX_RECORDS; i++) free_list[i] = i;
int db_insert(int id, const char* name, float value) {
if (free_count == 0) return -1; // no space
int idx = free_list[--free_count];
pool[idx].id = id;
strncpy(pool[idx].name, name, sizeof(pool[idx].name)-1);
pool[idx].value = value;
pool[idx].active = 1;
return idx;
}
Etsi ja päivitä
Yksinkertainen haku iteroi yli poolin, tarkistamalla vain aktiivisia tietueita. Päivityksiä varten, paikantaa tietueen, muokata kenttiä, ja valinnaisesti tarkistaa vapaa-listan, jos tietue on poistettu.
int db_find_by_id(int id) {
for (int i = 0; i < MAX_RECORDS; i++) {
if (pool[i].active && pool[i].id == id) return i;
}
return -1;
}
void db_update(int idx, float new_value) {
if (idx >= 0 && idx < MAX_RECORDS && pool[idx].active)
pool[idx].value = new_value;
}
Kehittyneet käsitteet: Indeksointi ja pysyvyys
Kun tietueiden määrä kasvaa, lineaarinen haku tulee kalliiksi. Lisäämällä yksinkertainen hakemisto. Kuten lajiteltu joukko avainosoittimia tai binary hakupuu. parantaa hakuaikaa. Sulautetut järjestelmät, staattinen array lajiteltu avain binary haku on usein riittävä, jos insertit ovat harvinaisia.
Järjestetty indeksi binary-haulla
Säilytä rinnakkaista tietueiden sarjaa hakuavaimen mukaan (esim. ID). Kun lisäät uuden tietueen, lisää sen indeksi lajiteltuun järjestelmään binäärisen syöttämisen avulla. Sitten haku tulee O(log n) binäärihaun kautta. Poistaminen vaatii indeksin siirtämistä, mutta pienten tietokantojen kohdalla tämä on hyväksyttävää.
Pysyvä tallennus tiedoston I/O avulla
Mikroohjaimissa ilman tiedostojärjestelmää, raa'at flash-muistikirjoitukset ovat yleisiä. Linux-pohjaisissa sulautettuissa järjestelmissä standardi POSIX /[[]/[[] toimii hyvin. Käytä yksinkertaista tiedostomuotoa: kirjoita otsikko (magic number, version, ennätysluku) ja sitä seuraa raaka-allas. Kolaroinnin kestokyvyn osalta kirjoitettava logi (WAL) voi auttaa, mutta perusmoottoreille riittää yksi atomikirjoitus (joka sopii yhteen flash-sivuun). FreeRTOS+FAT on kevyt tiedostojärjestelmä, joka toimii syvästi upotettujen järjestelmien osalta.
void db_save(const char* filename) {
FILE* fp = fopen(filename, "wb");
if (!fp) return;
fwrite(pool, sizeof(pool), 1, fp);
fclose(fp);
}
void db_load(const char* filename) {
FILE* fp = fopen(filename, "rb");
if (!fp) return;
fread(pool, sizeof(pool), 1, fp);
fclose(fp);
// Rebuild free-list from pool
free_count = 0;
for (int i = 0; i < MAX_RECORDS; i++) {
if (!pool[i].active) free_list[free_count++] = i;
}
}
Rajoitusten ja kompromissien käsittely
Upotettujen tietokantamoottoreiden välillä on jatkuva vaihtosuhde ominaisuuksien ja resurssien käytön välillä. Valitseminen mitkä ominaisuudet sisältyvät sovellukseen:
- ACID compliance[ . ... .....................................................................................................................................................................................................................................
- Indeksointi[ . Lisää lisää kustannukset, mutta nopeuttaa lukemista. Kirjoitus-raskaat työmäärät, ohittaa indeksit.
- valuutta[ . Useimmat sulautetut järjestelmät käyttävät yhtä lankaa. Vivutus muteksit, jos käytetään RTOS.
- Muistin jalanjälki[ . Staattinen jako on dynaamisempaa . Käytä vakioita puskurikoolle.
- Power loss[ ... ......................................................................................................................................................................................................................................
Käytännön esimerkki: Lämpötilan lokitietokanta
Harkitse IoT-lämpötila-anturia, joka tallentaa lukemat joka minuutti ja tallentaa ne paikallisesti 24 tunnin ajan. Tietokannan moottorin on käsiteltävä 1440 tietuetta (yksi minuutissa). Jokaisessa tietueessa voi olla aikaleima (Unix-aika), lämpötila (liukuva), ja sensorin tunnus. Kiinteän ja 256-paikkaisen reservin käyttö on liian pieni; tässä tarvitaan [. 28 tavua per tietue (4+4+4+4 ylipaine), allas käyttää noin 40 KB, mahdollista monissa mikroohjaimissa 128 KB RAM.
Moottori voi tallentaa tietoja rengas-puskuri tavalla: kun allas on täynnä, vanhin tietue on ylikirjoitettu. Toteuta "pää" osoitin seuraavaa kirjoittaa slot ja "häntä" vanhin aktiivinen tietue. Tämä välttää vapaa lista logiikka ja tarjoaa O(1) lisätä. Etsi voidaan optimoida binäärihaun aikaleimat, jos tietueet tallennetaan aikajärjestyksessä.
typedef struct {
uint32_t timestamp;
float temp_c;
uint8_t sensor_id;
uint8_t active; // not needed if using ring buffer
} TemperatureRecord;
#define MAX_LOGS 1440
TemperatureRecord logs[MAX_LOGS];
uint16_t head = 0; // next write position
uint16_t count = 0; // number of valid records
Lukeman lisääminen: kirjoita osoitteeseen , lisäys modulo [, lisäys []. Etsitään tiettyä aikaleimaa: jos lasketaan = = MAX LOGS, loki on rinnakkaisjakso päästä päähän-1 (kääre). Käytä binäärihakua virtuaalisen alkulaskennan jälkeen. Tämä malli on erittäin kevyt ja laajalti käytetty telemetriajärjestelmissä. Ring puskurin perusominaisuudet [ tarjoavat lisäasetukset.
Testaaminen ja optimointi kohdelaitteistolla
Testaa aina tietokantamoottoria varsinaisella sulautetulla laitteistolla. Emulaattorit eivät näe ajoitusrajoituksia, erityisesti flash-kirjoitussyklien ja tehonmenetysskenaarioiden osalta. Seuraa RAM-muistin käyttöä profilointityökalulla ja tarkista reunatapaukset: täydellinen tallennus, vaurioitunut data, nollaus mid-write. Yksinkertainen testivaljakko käyttää tuhansia satunnaisia inserttejä, hakuja ja poistoja verrattaessa kultaiseen malliin.
- Salaman kuluminen tasaus[ ... .....................................................................................................................................................................................................................................
- Power-Fail safe[] ... Käytä toimitusmerkkiä: kirjoita lipputavu täydellisen tietueerän jälkeen. Käynnistä uudelleen lippu; jos se puuttuu, hävitä viimeinen erä ja palaa edelliseen tilaan.
- Muistin yhdistämä[ . Vältä syvä rekursio. Pidä funktiokutsu pinot matala. Käytä staattisia puskureita tiedoston I/O.
Päätelmät
Perustietokannan moottorin rakentaminen sulautettuihin järjestelmiin C on käytännöllinen lähestymistapa tiedon hallintaan resurssirajoitteisissa laitteissa. Keskittymällä yksinkertaisiin tietorakenteisiin, kuten kiinteisiin altaat ja rengaspuskurit, kehittäjät saavuttavat tehokkaan CRUD-toiminnan minimaalisella yläpuolella. Lisäten valinnaisen indeksoinnin ja perusjatkeisuuden muuntaa yksinkertaisen järjestelmän luotettavaksi paikalliseksi datavarastoksi. Tässä kuvatut tekniikat pienistä sensorilokilaitteista kehittyneempiin järjestelmiin, ja ne tarjoavat perustan ymmärtää, miten suuremmat sulautetut tietokannat kuten SQLite tai Berkeley DB toimivat konepellin alla.