Saya menulis skema untuk database bank sederhana. Berikut spesifikasi dasar:
- Basis data akan menyimpan transaksi terhadap pengguna dan mata uang.
- Setiap pengguna memiliki satu saldo per mata uang, sehingga setiap saldo hanyalah jumlah dari semua transaksi terhadap pengguna dan mata uang tertentu.
- Saldo tidak boleh negatif.
Aplikasi bank akan berkomunikasi dengan database secara eksklusif melalui prosedur tersimpan.
Saya berharap database ini menerima ratusan ribu transaksi baru per hari, serta menyeimbangkan kueri pada tingkat yang lebih tinggi. Untuk melayani saldo dengan sangat cepat, saya perlu melakukan pra-agregat. Pada saat yang sama, saya perlu menjamin bahwa saldo tidak pernah bertentangan dengan riwayat transaksinya.
Pilihan saya adalah:
Memiliki
balances
tabel terpisah dan lakukan salah satu dari yang berikut:Terapkan transaksi ke tabel
transactions
danbalances
. GunakanTRANSACTION
logika di lapisan prosedur tersimpan saya untuk memastikan bahwa saldo dan transaksi selalu sinkron. (Didukung oleh Jack .)Terapkan transaksi ke
transactions
tabel dan miliki pemicu yang memperbaruibalances
tabel untuk saya dengan jumlah transaksi.Terapkan transaksi ke
balances
tabel dan miliki pemicu yang menambahkan entri baru ditransactions
tabel untuk saya dengan jumlah transaksi.
Saya harus mengandalkan pendekatan berbasis keamanan untuk memastikan tidak ada perubahan yang dapat dilakukan di luar prosedur yang tersimpan. Kalau tidak, misalnya, beberapa proses dapat langsung memasukkan transaksi ke dalam
transactions
tabel dan di bawah skema1.3
saldo yang relevan akan tidak sinkron.Memiliki tampilan yang
balances
diindeks yang mengagregasi transaksi dengan tepat. Saldo dijamin oleh mesin penyimpanan untuk tetap selaras dengan transaksi mereka, jadi saya tidak perlu bergantung pada pendekatan berbasis keamanan untuk menjamin ini. Di sisi lain, saya tidak bisa memaksakan saldo menjadi non-negatif lagi karena pandangan - bahkan pandangan yang diindeks - tidak dapat memilikiCHECK
kendala. (Didukung oleh Denny .)Hanya punya
transactions
meja tetapi dengan kolom tambahan untuk menyimpan saldo efektif setelah transaksi itu dijalankan. Dengan demikian, catatan transaksi terbaru untuk pengguna dan mata uang juga mengandung saldo mereka saat ini. (Disarankan di bawah oleh Andrew ; varian yang diajukan oleh garik .)
Ketika saya pertama kali ditangani masalah ini, saya membaca ini dua diskusi dan memutuskan pada pilihan 2
. Untuk referensi, Anda dapat melihat implementasi sederhana di sini .
Sudahkah Anda merancang atau mengelola basis data seperti ini dengan profil beban tinggi? Apa solusi Anda untuk masalah ini?
Apakah Anda pikir saya sudah membuat pilihan desain yang tepat? Apakah ada sesuatu yang harus saya ingat?
Misalnya, saya tahu perubahan skema pada
transactions
tabel akan mengharuskan saya membangun kembalibalances
tampilan. Sekalipun saya mengarsipkan transaksi untuk menjaga basis data kecil (misalnya dengan memindahkannya ke tempat lain dan menggantinya dengan transaksi ringkasan), harus membangun kembali pandangan dari puluhan juta transaksi dengan setiap pembaruan skema mungkin akan berarti lebih banyak downtime per penyebaran.Jika tampilan yang diindeks adalah cara untuk pergi, bagaimana saya bisa menjamin bahwa tidak ada saldo negatif?
Pengarsipan transaksi:
Biarkan saya menguraikan sedikit tentang pengarsipan transaksi dan "ringkasan transaksi" yang saya sebutkan di atas. Pertama, pengarsipan teratur akan menjadi kebutuhan dalam sistem beban tinggi seperti ini. Saya ingin menjaga konsistensi antara saldo dan riwayat transaksi mereka sambil memungkinkan transaksi lama dipindahkan ke tempat lain. Untuk melakukan ini, saya akan mengganti setiap kumpulan transaksi yang diarsipkan dengan ringkasan jumlah mereka per pengguna dan mata uang.
Jadi, misalnya, daftar transaksi ini:
user_id currency_id amount is_summary
------------------------------------------------
3 1 10.60 0
3 1 -55.00 0
3 1 -12.12 0
diarsipkan dan diganti dengan ini:
user_id currency_id amount is_summary
------------------------------------------------
3 1 -56.52 1
Dengan cara ini, keseimbangan dengan transaksi yang diarsipkan mempertahankan riwayat transaksi yang lengkap dan konsisten.
sumber
Jawaban:
Saya tidak terbiasa dengan akuntansi, tetapi saya memecahkan beberapa masalah serupa di lingkungan tipe inventaris. Saya menyimpan total yang berjalan di baris yang sama dengan transaksi. Saya menggunakan batasan, sehingga data saya tidak pernah salah bahkan di bawah konkurensi tinggi. Saya telah menulis solusi berikut pada tahun 2009 :
Menghitung total yang berjalan sangat lambat, apakah Anda melakukannya dengan kursor atau gabungan segitiga. Sangat menggoda untuk melakukan denormalisasi, untuk menyimpan total yang berjalan dalam kolom, terutama jika Anda sering memilihnya. Namun, seperti biasa ketika Anda melakukan denormalkan, Anda perlu menjamin integritas data yang didenormalisasi Anda. Untungnya, Anda dapat menjamin integritas menjalankan total dengan kendala - selama semua kendala Anda tepercaya, semua total menjalankan Anda benar. Juga dengan cara ini Anda dapat dengan mudah memastikan bahwa saldo saat ini (total berjalan) tidak pernah negatif - menegakkan dengan metode lain juga bisa sangat lambat. Script berikut menunjukkan tekniknya.
sumber
Tidak mengizinkan pelanggan untuk memiliki saldo kurang dari 0 adalah aturan bisnis (yang akan berubah dengan cepat karena biaya untuk hal-hal seperti over draft adalah bagaimana bank menghasilkan sebagian besar uang mereka). Anda akan ingin menangani ini dalam pemrosesan aplikasi ketika baris dimasukkan ke dalam riwayat transaksi. Terutama karena Anda mungkin berakhir dengan beberapa pelanggan memiliki proteksi cerukan dan beberapa mendapatkan biaya tambahan dan beberapa tidak mengizinkan jumlah negatif dimasukkan.
Sejauh ini saya suka ke mana Anda akan pergi dengan ini, tetapi jika ini untuk proyek yang sebenarnya (bukan sekolah) harus ada banyak pemikiran dimasukkan ke dalam aturan bisnis, dll. Setelah Anda memiliki sistem perbankan naik dan menjalankan tidak ada banyak ruang untuk mendesain ulang karena ada undang-undang yang sangat spesifik tentang orang yang memiliki akses ke uang mereka.
sumber
Pendekatan yang sedikit berbeda (mirip dengan opsi kedua Anda ) untuk dipertimbangkan adalah hanya memiliki tabel transaksi, dengan definisi:
Anda mungkin juga menginginkan ID transaksi / Pesanan, sehingga Anda dapat menangani dua transaksi dengan tanggal yang sama dan meningkatkan permintaan pengambilan Anda.
Untuk mendapatkan saldo saat ini, yang perlu Anda dapatkan adalah catatan terakhir.
Metode untuk mendapatkan catatan terakhir :
Kekurangan:
Transaksi untuk Pengguna / Mata Uang perlu diserialisasi untuk menjaga keseimbangan yang akurat.
Pro:
Sunting: Beberapa contoh pertanyaan tentang pengambilan saldo saat ini dan untuk menyorot con (Terima kasih @Jack Douglas)
sumber
SELECT TOP (1) ... ORDER BY TransactionDate DESC
akan sangat sulit untuk menerapkan dalam sedemikian rupa sehingga SQL Server tidak terus-menerus memindai tabel transaksi. Alex Kuznetsov memposting solusi di sini untuk masalah desain serupa yang melengkapi jawaban ini dengan sempurna.Setelah membaca diskusi itu juga, saya tidak yakin mengapa Anda memutuskan pada solusi DRI atas pilihan paling masuk akal yang Anda uraikan:
Solusi semacam ini memiliki manfaat praktis yang sangat besar jika Anda memiliki kemewahan membatasi semua akses ke data melalui API transaksional Anda. Anda kehilangan manfaat yang sangat penting dari DRI, yaitu integritas dijamin oleh basis data, tetapi dalam model kompleksitas apa pun akan ada beberapa aturan bisnis yang tidak dapat ditegakkan oleh DRI .
Saya akan menyarankan menggunakan DRI jika memungkinkan untuk menegakkan aturan bisnis tanpa terlalu banyak menekuk model Anda untuk memungkinkannya:
Segera setelah Anda mulai mempertimbangkan mencemari model Anda seperti ini, saya pikir Anda pindah ke daerah di mana manfaat DRI lebih besar daripada kesulitan yang Anda hadapi. Pertimbangkan misalnya bahwa bug dalam proses pengarsipan Anda secara teori dapat menyebabkan aturan emas Anda (yang menyeimbangkan selalu sama dengan jumlah transaksi) pecah secara diam - diam dengan solusi DRI .
Berikut ini ringkasan keuntungan dari pendekatan transaksional seperti yang saya lihat:
--sunting
Untuk memungkinkan pengarsipan tanpa menambah kerumitan atau risiko, Anda dapat memilih untuk menyimpan baris ringkasan di tabel ringkasan terpisah, dihasilkan terus menerus (meminjam dari @Andrew dan @Garik)
Misalnya, jika ringkasannya bulanan:
sumber
Nick.
Gagasan utama adalah menyimpan saldo dan catatan transaksi dalam tabel yang sama. Itu terjadi secara historis, saya pikir. Jadi dalam hal ini kita bisa mendapatkan keseimbangan hanya dengan menemukan catatan ringkasan terakhir.
Varian yang lebih baik adalah mengurangi jumlah catatan ringkasan. Kami dapat memiliki satu catatan saldo di akhir (dan / atau mulai) hari itu. Seperti yang Anda tahu setiap bank harus
operational day
membuka dan menutupnya untuk melakukan beberapa operasi ringkasan untuk hari ini. Ini memungkinkan kita untuk menghitung bunga dengan mudah menggunakan catatan saldo setiap hari, misalnya:Keberuntungan.
sumber
Berdasarkan kebutuhan Anda, opsi 1 akan muncul yang terbaik. Meskipun saya ingin desain saya hanya memungkinkan memasukkan ke dalam tabel transaksi. Dan memiliki pemicu pada tabel transaksi, untuk memperbarui tabel saldo real time. Anda bisa menggunakan izin basis data untuk mengontrol akses ke tabel ini.
Dalam pendekatan ini, saldo waktu nyata dijamin akan disinkronkan dengan tabel transaksi. Dan tidak masalah jika prosedur tersimpan atau psql atau jdbc digunakan. Anda dapat memeriksa saldo negatif Anda jika diperlukan. Kinerja tidak akan menjadi masalah. Untuk mendapatkan keseimbangan waktu nyata, ini adalah permintaan tunggal.
Pengarsipan tidak akan memengaruhi pendekatan ini. Anda dapat memiliki tabel ringkasan mingguan, bulanan, tahunan juga jika diperlukan untuk hal-hal seperti laporan.
sumber
Di Oracle, Anda bisa melakukan ini hanya dengan menggunakan tabel transaksi dengan tampilan terwujud cepat yang dapat di-refresh di dalamnya yang melakukan agregasi untuk membentuk saldo. Anda menentukan pemicu pada Tampilan Terwujud. Jika Tampilan Terwujud didefinisikan dengan 'ON COMMIT', itu secara efektif mencegah menambah / memodifikasi data dalam tabel dasar. Pemicu mendeteksi data yang valid dan memunculkan pengecualian, di mana ia mengembalikan transaksi. Contoh yang bagus ada di sini http://www.sqlsnippets.com/en/topic-12896.html
Saya tidak tahu sqlserver tetapi mungkin memiliki opsi serupa?
sumber