
Catatan singkat tentang basis data nilai kunci tertanam yang disebut Coffer
ditulis dalam Golang. Jika sangat singkat: ketika database dalam keadaan berhenti, data ada di disk, saat mulai, data disalin ke memori. Membaca berasal dari ingatan. Selama perekaman, data memori diubah, dan perubahan ditulis ke log disk. Ukuran maksimum dari data yang disimpan dibatasi oleh ukuran RAM. API memungkinkan Anda membuat header untuk catatan basis data dan menggunakannya dalam transaksi, sambil menjaga konsistensi data.
Tapi pertama-tama, sedikit pengantar lirik. Sekali waktu, ketika rumput lebih hijau, saya perlu menanamkan basis data nilai kunci untuk aplikasi-go. Melihat sekeliling dan menabrak paket yang berbeda, saya entah bagaimana tidak menemukan apa yang saya inginkan (secara subyektif), dan hanya menerapkan solusi dengan database relasional eksternal. Solusi kerja yang bagus. Tetapi seperti yang mereka katakan, sendok ditemukan, tetapi endapan tetap ada. Pertama-tama, saya ingin yang asli, yang ditulis di Go database, langsung asli-asli. Dan ada yang seperti itu, hanya terlihat luar biasa. Namun, tidak ada satu juta dari mereka. Ini bahkan mengejutkan ketika Anda menganggap bahwa seorang programmer jarang di dunia yang tidak menulis database, kerangka kerja atau permainan kasual dalam hidupnya.
Nah, Anda dapat mencoba dan menumpuk sepeda Anda di lutut Anda, dengan blackjack dan barang lainnya. Pada saat yang sama, semua orang tahu, atau setidaknya menebak, bahwa menulis bahkan basis data nilai kunci yang sederhana tampak sederhana hanya pada pandangan pertama. Tetapi pada kenyataannya, semuanya jauh lebih menyenangkan (dan itu terjadi). Dan saya ingin tahu tentang ACID dan khawatir tentang transaksi. Transaksi sejati lebih mungkin dalam arti finansial, karena Saya kemudian sibuk di fintech.
Keamanan data
Pertimbangkan kasus ketika, selama pengoperasian aplikasi dengan rekaman aktif, catu daya di komputer ditutupi dengan baskom tembaga dan disk tidak pecah. Jika saat ini aplikasi diterima ok
dari database, maka data dari operasi ini tidak akan hilang. Jika aplikasi menerima jawaban negatif, maka tentu saja, operasi tidak selesai. Nah, kasus di mana aplikasi mengirim permintaan, tetapi tidak menerima respons: operasi ini kemungkinan besar tidak selesai, tetapi ada kemungkinan kecil bahwa operasi jatuh ke log, tetapi tepat pada saat respons dikirim, pemadaman listrik terjadi.
Bagaimana pada kasus terakhir untuk mengetahui apa yang terjadi dengan operasi terakhir? Ini pertanyaan yang menarik. Secara tidak langsung, Anda dapat menebaknya (menarik kesimpulan) dengan melihat nilai catatan kepentingan setelah peluncuran aplikasi baru dari database. Namun, jika operasi cukup sering, saya khawatir itu tidak akan membantu. Anda dapat melihat file log terakhir (itu akan dengan angka tertinggi), tetapi secara manual ini tidak nyaman. Saya pikir, di masa depan, Anda dapat menambahkan kemampuan untuk melihat log di API (tentu saja, log dalam hal ini tidak boleh dihapus).
Terus terang, saya sendiri tidak mencabut kabelnya dari soket, karena Saya tidak ingin mengambil risiko besi demi memeriksa database. Dalam pengujian, saya hanya merusak file log normal, dan dalam hal ini, semuanya terjadi seperti yang saya harapkan. Namun, tidak ada pengalaman dalam penggunaan praktis dari database, itu tidak bekerja di prod, dan ada risiko. Namun, untuk proyek kesayangan, saya pikir database dapat digunakan tanpa rasa takut. Secara umum, penafian yang biasa, tidak ada jaminan.
Basis data saat ini tidak terlindungi dari digunakan dalam dua aplikasi yang berbeda (atau yang sama, itu tidak masalah di sini) dikonfigurasi untuk bekerja dengan direktori yang sama. Mohon pertimbangkan momen ini! Namun, karena basis datanya built-in, lalu melewatinya semacam jenis referensi dalam argumen, jelas tidak ada gunanya mengubahnya di suatu tempat di paralel goroutine.
Konfigurasi
Basis data memiliki beberapa parameter yang dapat dikonfigurasikan, namun, hampir semuanya memiliki nilai default, sehingga semuanya dapat masuk ke dalam satu baris pendek cof, err, wrn := Db(dirPath).Create()
Kesalahan dikembalikan (jika terjadi kesalahan, pekerjaan lebih lanjut dengan database dilarang) dan peringatan, yang bisa Anda ketahui, tetapi ini tidak mengganggu operasi database.
Saya tidak akan mengacaukan teks dengan deskripsi rumit, jika perlu, silakan melihatnya di readme dari repositori - github.com/claygod/coffer/blob/master/README_RU.md#config Perhatikan metode Handler yang menghubungkan penangan untuk transaksi, saya akan menulis beberapa baris tentang hal itu lebih rendah, di sini saya hanya daftar mereka:
- Db (dirPath)
- BatchSize (batchSize)
- LimitRecordsPerLogfile (limitRecordsPerLogfile)
- FollowPause (100 * waktu. Kedua)
- LogByCheckpoint (1000)
- AllowStartupErrLoadLogs (true)
- MaxKeyLength (maxKeyLength)
- MaxValueLength (maxValueLength)
- MaxRecsPerOperation (1.000.000)
- RemoveUnlessLogs (true)
- LimitMemory (100 * 1.000.000)
- LimitDisk (1000 * 1.000.000)
- Handler ("handler1", & handler1)
- Handler ("handler2", & handler2)
- Penangan (map [string] * penangan)
- Buat ()
API
Sejauh mungkin, saya membuat API sederhana, dan untuk basis nilai kunci, jangan terlalu pintar:
- Mulai - mulai database
- Hentikan - hentikan basis data
- StopHard - berhenti terlepas dari operasi yang sedang dilakukan saat ini (mungkin saya akan menghapusnya)
- Save - menyimpan snapshot dari kondisi database saat ini
- Tulis - tambahkan satu catatan ke basis data
- WriteList - menambahkan beberapa catatan ke basis data (mode ketat dan opsional)
- WriteListUnsafe - menambahkan beberapa catatan ke basis data tanpa memperhatikan keamanan data
- Baca - dapatkan satu catatan dengan kunci
- ReadList - dapatkan daftar catatan
- ReadListUnsafe - dapatkan daftar catatan tanpa memperhatikan keamanan data
- Hapus - hapus satu catatan
- DeleteList - menghapus banyak rekaman dalam mode ketat / opsional
- Transaksi - melakukan transaksi
- Hitung - berapa banyak catatan dalam database
- CountUnsafe - berapa banyak catatan dalam database (sedikit lebih cepat, tetapi tidak aman)
- RecordsList - daftar semua kunci basis data
- RecordsListUnsafe - daftar semua kunci basis data (sedikit lebih cepat, tetapi tidak aman)
- RecordsListWithPrefix - daftar kunci dengan awalan yang ditentukan
- RecordsListWithSuffix - daftar kunci dengan ujung yang ditentukan
Penjelasan singkat tentang API:
- Mode ketat - lakukan semuanya atau tidak sama sekali.
- Mode opsional - lakukan semua yang berfungsi.
- StopHard - mungkin metode ini harus dihapus dari API sampai diputuskan.
- Semua metode RecordsList tidak cepat, karena Tidak ada indeks di toko sekarang, sementara ini adalah fullscan.
- Semua metode tidak aman lebih cepat, tetapi konsistensi tidak tersirat saat menggunakannya. Adalah logis untuk menggunakannya pada DB yang berhenti untuk pengisian cepat atau sesuatu yang lain dalam nada yang sama.
- Pengikut memantau pembaruan snapshot basis data secara teratur, jadi metode Simpan kemungkinan besar untuk beberapa kasus khusus ketika Anda benar-benar ingin membuat snapshot baru (sampai kasus seperti itu muncul di benak saya, tapi mungkin itu).
Kasus penggunaan sederhana:
package main import ( "fmt" "github.com/claygod/coffer" ) const curDir = "./" func main() {
Transaksi
Seperti disebutkan di atas, definisi transaksi saya mungkin tidak sesuai dengan yang diterima secara umum dalam konstruksi DB, mungkin mereka disatukan hanya oleh sebuah ide. Dalam implementasi tertentu, transaksi adalah header tertentu yang ditentukan pada tahap konfigurasi basis data (metode Handler
). Saat kami memohon transaksi dengan header ini, database memblokir catatan yang akan bekerja dengan header dan mentransfer nilainya saat ini ke header. Header memanipulasi data ini sesuai kebutuhan, dan mengembalikan nilai-nilai baru dari database, dan itu menyimpannya dalam seratus. Setelah itu, catatan tidak terkunci dan tersedia untuk operasi lain.
Ada contoh dalam repo yang mengungkapkan esensi dari menggunakan transaksi dengan sangat baik. Karena penasaran, saya membuat contoh keuangan kecil, di mana ada operasi debit dan kredit, transfer, pembelian dan penjualan. Sangat mudah untuk menulis contoh ini, dan pada saat yang sama implementasi setinggi lutut ini cukup konsisten dan cocok untuk digunakan dalam berbagai solusi keuangan, atau misalnya dalam bidang logistik.
Poin penting: kode handler tidak disimpan dalam database. Saya mempunyai ide untuk menyimpannya dalam log, tetapi bagi saya sepertinya terlalu boros, jadi saya tidak mempersulitnya, dan karenanya tanggung jawab untuk konsistensi penangan antara basis data yang berbeda terletak pada pengembang kode yang menggunakan basis data. Penangan pasti tidak dapat diubah jika aplikasi dan database berhenti mogok. Dalam hal ini, Anda harus terlebih dahulu memulai database dan kemudian menghentikannya secara teratur - snapshot data baru akan dibuat. Agar tidak bingung, saya menyarankan Anda untuk menggunakan nomor versi atas nama penangan.
Terima dan proses tanggapan
Database mengembalikan laporan yang menunjukkan status respons dan data. Karena ada banyak kode, dan menulis sakelar dengan pemrosesan masing-masingnya merepotkan, Anda mungkin ingin memeriksa kira-kira. Ini seharusnya tidak dilakukan. Faktanya adalah bahwa kode tersebut mungkin memiliki status Ok, Error, Panic. Dengan Ok, semuanya jelas, tapi bagaimana dengan dua lainnya? Jika statusnya Kesalahan, operasi tertentu selesai, atau tidak selesai. Kesalahan ini harus ditangani dengan tepat dalam aplikasi. Namun, pekerjaan lebih lanjut dengan database dimungkinkan (dan perlu). Hal lain Panic - bekerja dengan database harus dihentikan.
Memeriksa IsCodeError
menyederhanakan pekerjaan dengan semua kesalahan, jadi jika Anda tidak tertarik dengan detailnya, lanjutkan bekerja.
Pemeriksaan IsCodePanic
mencakup semua kasus di mana pekerjaan dengan database harus dihentikan.
Dalam kasus sederhana, tiga saklar sudah cukup untuk memproses respons:
IsCodeOk
- terus bekerja seperti IsCodeOk
IsCodeError
- mencatat kesalahan dari laporan dan bekerja lebih lanjutIsCodePanic
- mencatat kesalahan dari laporan dan berhenti bekerja dengan database
Offtop
Untuk nama, salah satu opsi untuk menerjemahkan
kata ke dalam bahasa Inggris dipilih, saya lebih suka box
, tentu saja, tapi ini kata yang terlalu populer, saya harap coffer
akan coffer
.
Topik dengan ACID menurut saya agak holistik, jadi saya akan mengatakan bahwa Coffer berkomitmen untuk ini, tetapi bukan fakta, dan saya tidak mengklaim bahwa ia berhasil.
Performa
Saya segera menulis database dengan mempertimbangkan konkurensi dan kompetisi. Dalam mode ini ia menunjukkan keefektifannya (meskipun ini mungkin dikatakan terlalu keras). Dalam hasil di bawah ini, patokan menunjukkan bandwidth 200k rps. Ini tentu saja bangku buatan, dan kenyataannya akan sangat berbeda, karena banyak tergantung pada ukuran data yang direkam, jumlah data yang sudah direkam, kinerja besi dan fase bulan. Tapi tren itu setidaknya bisa dimengerti. Jika database digunakan dalam mode single-threaded, setiap permintaan dieksekusi hanya setelah menerima jawaban dari yang sebelumnya, kecepatannya akan lambat, dan saya akan menyarankan Anda untuk mencari di database lain, tetapi tidak Coffer.
- BenchmarkCofferTransactionSequence-4 2000 227928 ns / op
- BenchmarkCofferTransactionPar32HalfConcurent-4 100000 4199 ns / op
Ngomong-ngomong, jika seseorang menghabiskan waktu dan cenderung melakukan repositori dengan Coffer, jika mungkin, jalankan bangku yang terletak di dalamnya. Saya sangat tertarik dengan mesin apa yang akan ditampilkan kinerja databasenya. Pertama-tama, tentu saja semuanya tergantung pada disk. Ini menjadi sangat jelas bagi saya setelah saya baru saja membeli Samsung EVO baru. Tapi jangan khawatir, ini bukan pengganti disk mati. Toshiba lama terus melayani dengan benar dan sekarang menyimpan arsip video saya.
Jam tangan yang terpasang di dalam memori masih berupa peta sederhana, bahkan tidak dibagi menjadi beberapa bagian. Tentu saja, sangat bagus untuk memperbaikinya, misalnya, untuk membuatnya cepat memilih kunci dengan awalan dan sufiks. Meskipun saya tidak melakukan ini, tk. fungsi utama, seperti yang saya katakan chip DB, saya lihat dalam transaksi, dan hambatan dalam kinerja untuk transaksi akan bekerja dengan disk, dan hanya kemudian, bekerja dengan memori.
Lisensi
Sekarang lisensi memungkinkan Anda untuk menyimpan hingga sepuluh juta catatan dalam database, bagi saya tampaknya ini adalah angka yang cukup. Rencana lebih lanjut untuk pengembangan database sedang dalam proses pembentukan.
Secara umum, menarik bagi saya untuk menggunakan database sebagai paket, dan fokus utamanya pada API-nya.
Kesimpulan
Baru-baru ini, saya sering menjumpai tugas menulis layanan dengan karakteristik ketersediaan tinggi. Sayangnya, karena fakta bahwa ini hampir selalu menyiratkan adanya beberapa contoh, tidak layak menggunakan basis data bawaan dengan kasus seperti itu. Masih ada opsi untuk aplikasi atau layanan reguler yang ada dalam satu contoh. Bagi saya, ini merupakan kasus yang lebih jarang, tetapi bagaimanapun juga, dan dalam kasus seperti itu, sangat baik untuk memiliki database yang mencoba, sejauh mungkin, untuk menyimpan data yang tersimpan di dalamnya. Peti yang saya buat sedang mencoba memecahkan masalah seperti itu. Mari kita lihat bagaimana dia melakukannya.
Ucapan Terima Kasih
- Untuk semua orang yang membaca artikel sampai akhir
- Komentator ingin membagikan pendapat mereka
- Dikirim dalam info pribadi tentang kesalahan ketik dan kesalahan dalam teks
- Tetangga menyalakan musik di malam hari
Referensi
Repositori DB
Deskripsi dalam bahasa Rusia