Table of Contents
Sistem Embedded Bebeed sering kali membutuhkan manajemen data lokal tanpa overhead dari server basis data penuh. Implementasi mesin basis data ringan di C memberikan pengembang kontrol langsung atas memori, kinerja, dan penyimpanan. Artikel ini berjalan melalui desain dan implementasi dari mesin basis data tertanam sederhana, meliputi struktur data, operasi CRUD, pengindeksan, dan strategi kegigihan untuk lingkungan yang terkonstrain sumber daya.
Persyaratan Inti Persyaratan untuk Mesin Basis Data Terbenam
Mesin basis data yang tertanam harus beroperasi dalam batas ketat pada RAM, flash, dan kecepatan pemrosesan.Persyaratan khas termasuk perilaku deterministik, jejak kode minimal, dan tanpa dependensi eksternal. Mesin harus mendukung operasi dasar: sisip, retrieve, update, hapus, dan pencarian.Banyak basis data tertanam juga perlu bertahan dari kehilangan daya dan menyimpan data pada memori non-volatile seperti EEPROM, SPI flash, atau kartu SD.
Kegunaan struktur data yang tepat adalah keputusan desain pertama. Array sederhana tetapi dibatasi oleh ukuran statis. Senarai terpaut memungkinkan pertumbuhan dinamis tetapi menambah penunjuk overhead. Untuk kinerja yang seimbang, pendekatan hibrida menggunakan map record fixed-size dengan free-list dapat bekerja dengan baik.]SQLite's design principles]] menawarkan wawasan yang berguna bahkan untuk banyak mesin yang lebih sederhana.
Sia - Benda yang Mendesain Lapisan Penyimpanan Rekam
lapisan penyimpanan mengelola bagaimana catatan disebar dalam memori atau disk. Pola umum adalah untuk memperlakukan setiap record sebagai struktur panjang-tetap untuk memudahkan aritmetik penunjuk dan memungkinkan pengindeksan langsung. Catatan panjang variabel memperumit fragmentasi dan membutuhkan manajer memori.
Catatan Tetap-Panjang-Kepanjangan dengan Kolam Rekor
Diafine sejumlah maksimum catatan (contoh, ) dan mengalokasikan sebuah array statis. Sebuah bitmap atau trek daftar bebas yang slot digunakan. Ketika sebuah rekor dihapus, slotnya kembali ke kolam. Pendekatan ini menghindari alokasi dinamis dan menjamin waktu alokasi O(1).
#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];
Untuk penyimpanan yang gigih, kolam dapat ditopang oleh berkas atau wilayah flash. Pada start-up, mesin membaca kolam dari memori non-volatile menjadi RAM, dan pada shutdown (atau secara berkala) ia menulis kembali.
Cek Integritas Data
Tambah bidang checksum sederhana ke setiap rekor untuk mendeteksi korupsi. CRC-32 adalah pilihan yang baik untuk sistem embedded, menyeimbangkan kompleksitas dengan kekuatan deteksi kesalahan.
Mengimplementasi Operasi Dasar CRUD
. Dengan pool record didefinisikan, mengimplementasikan fungsi untuk menyisipkan, menemukan, memperbarui, dan menghapus catatan. Operasi pencarian sering kali adalah kinerja botneck, sehingga pencarian linear naif hanya dapat diterima untuk basis data kecil (beberapa ratus catatan).
Diselitkan dengan Manajemen Free-List
Ketahanan sebuah daftar bebas indeks. Pada sisipan, pop sebuah indeks dari daftar bebas, mengisi catatan, dan menandainya aktif. Daftar bebas itu sendiri dapat menjadi sebuah stack sederhana menggunakan sebuah array integer.
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;
}
Cari dan Kemaskini
Sebuah pencarian sederhana yang diiterasikan di atas kolam, memeriksa hanya catatan aktif. Untuk pemutakhiran, mencari catatan, mengubah medan, dan mungkin memeriksa ulang daftar bebas jika catatan dihapus.
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;
}
Konsep Lanjutan: Berindeks dan Kekal
Dari jumlah catatan bertambah, pencarian linear menjadi mahal. Menambah indeks sederhana ⁇ seperti susunan yang diurutkan dari penunjuk kunci atau pohon pencarian biner ⁇ memperbaiki waktu penerimaan. Untuk sistem tertanam, sebuah array statis diurutkan pada kunci dengan pencarian biner sering kali cukup jika sisipan tidak berurutan.
Diurutkan dengan Pencarian Binari
Ketahanan sebuah array paralel dari indeks catatan yang diurutkan oleh kunci pencarian (misalnya, ID). Ketika menyisipkan sebuah catatan baru, masukkan indeksnya ke dalam susunan yang diurutkan menggunakan penyisipan biner. Kemudian pencarian menjadi O(log n) melalui pencarian biner. Deleti memerlukan pergeseran indeks array, tetapi untuk basis data kecil hal ini dapat diterima.
Penyimpanan Yang Tepat Memanfaatkan I/O Berkas
Pada mikrokontroler tool tanpa sistem berkas, tulis memori raw flash adalah umum. Pada sistem tertanam berbasis Linux, POSIX standar // bekerja dengan baik. Gunakan format berkas sederhana: menulis header (nomor magic, versi, hitungan rekaman) diikuti oleh array raw pool. Untuk ketahanan raw fall, log write-ahead (WAL) dapat membantu, tetapi untuk mesin dasar sebuah atom tulis (yang cocok dalam satu halaman flash) cukup.FLT:0]] [[FRT+FATFL]] adalah sebuah pilihan sistem yang tertanam secara mendalam.
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;
}
}
Kekangan Penanganan dan Perdagangan
Mesin basis data yang telah dibenamkan oleh embedded menghadapi perdagangan-off konstan antara fitur dan penggunaan sumber daya. Memilih fitur mana yang harus dimasukkan tergantung pada aplikasi:
- [[LALT:0]]ACID compliance[]] ⁇ Biasanya tidak diperlukan. Penulisan atom sederhana mencukupi untuk sebagian besar pencatatan data sensor.
- [[EfolfLT:0]]Indexing ⁇ Tambahkan biaya sisip tetapi kecepatan naik dibaca. Untuk beban kerja yang berat-tulis, skip indeks.
- [[LATGAL:0]]Konkurensi ⁇ Sistem paling tertanam menjalankan thread tunggal. Leverage muteks jika menggunakan RTOS.
- [[FolT:0]]Memorial trace ⁇ Peruntukan static lebih aman daripada dinamis . Gunakan konstanta untuk ukuran buffer.
- [[EfleksifLLT:0]]Power losss[]] ⁇ Untuk penyimpanan flash, hindari sering menulis kecil. Batch updates dan gunakan skema buffer ganda.
Contoh Praktis: Sebuah Basis Data Log Log Logger Suhu
Diagnose sebuah sensor suhu IoT yang mencatat pembacaan setiap menit dan menyimpannya secara lokal selama 24 jam. Mesin basis data harus menangani 1440 catatan (satu per menit). Setiap catatan mungkin berisi timestamp (Unix epoch), suhu (float), dan ID sensor. Menggunakan kolam rekam tetap dengan slot 256 terlalu kecil; di sini kita membutuhkan . Dengan 28 byte per record (4+4+4+4+4 untuk overhead), kolam renang menggunakan sekitar 40 KB, feasible pada banyak mikrokontroler 128 RAM dengan RAM.
Mesin kineralis dapat menyimpan data dalam mode buffer cincin: ketika kolam penuh, rekor tertua ditulis secara overwritten. Implementasi sebuah ⁇ kepala ⁇ penunjuk untuk slot tulis berikutnya dan a ⁇ tail ⁇ untuk catatan aktif tertua. Ini menghindari logika daftar bebas dan menyediakan sisipan O(1). Pencarian dapat dioptimalkan dengan pencarian biner pada timestamp jika catatan disimpan dalam urutan kronologis.
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
Mengisi sebuah bacaan: unakan ], inccrement modulo , inccrement (dicacat pada ). Mencari sebuah timestamp tertentu: jika dihitung == MAX LOGS, log adalah urutan berdampingan dari kepala ke kepala-1 (dibungkus). Gunakan pencarian biner setelah komputasi awal virtual. Pola ini sangat ringan dan digunakan secara luas dalam sistem telemetri. Ring buffer dasar] menyediakan pilihan implementasi tambahan.
Uji dan Optimasi Uji Kebidanan pada Perkakasan Target
Selalu uji mesin basis data pada perangkat keras tertanam yang sebenarnya. Emulator melewatkan batasan waktu, terutama untuk siklus penulisan flash dan skenario kehilangan daya. Monitor penggunaan RAM dengan alat profiling dan verifikasi kasus edge: penyimpanan penuh, data rusak, reset mid-write. Sebuah harness tes sederhana menjalankan ribuan sisipan acak, pencarian, dan hapus sambil membandingkan dengan model emas.
- [Efleksi]Flash memakai leveling]] ⁇ Jika menulis ke EEPROM atau NOR flash, batas total menulis ke beberapa ratus ribu. Buffer melingkar dengan lapisan aus-leveling memperpanjang umur.
- [[EUGHFLT:0]]Power-fail aman[]] ⁇ Gunakan tanda komitmen: menulis byte bendera setelah kumpulan catatan lengkap. Pada restart, periksa bendera; jika hilang, abaikan batch terakhir dan kembali ke keadaan sebelumnya.
- [[ZOBILT:0]]Memori pooling ⁇ Hindari rekursi mendalam. Jaga fungsi call stacks dangkal. Gunakan static buffers untuk berkas I/O.
Kekecualian Kesimpulan
Membina sebuah mesin basis data dasar di C untuk sistem tertanam adalah pendekatan praktis untuk mengelola data dalam perangkat terbatas sumber daya. Dengan berfokus pada struktur data sederhana seperti fixed-record pool dan buffer cincin, pengembang mencapai operasi CRUD yang efisien dengan overhead minimal. Penambahan indeks opsional dan kegigihan dasar mengubah susunan sederhana menjadi source datastore lokal yang handal. Teknik yang digambarkan di sini skala dari logger sensor kecil ke sistem yang lebih canggih, dan mereka menyediakan landasan untuk memahami bagaimana basis data tertanam yang lebih besar seperti SQLite atau Berkeley DB beroperasi di bawah kap.