Apa cara yang mungkin untuk menghindari duplikat ketika Anda tidak dapat menambahkan indeks unik

10

Saya terjebak dalam masalah konkurensi.

Merupakan masalah umum di mana pengguna mengirim 2 o 3 transaksi untuk mempertahankan beberapa data yang TIDAK HARUS diduplikasi dalam DB, jika ada catatan duplikat Anda harus mengembalikan kesalahan.

Masalah ini mudah ketika Anda bisa menambahkan indeks (unik) ke kolom tempat Anda menyimpan hash.

Tetapi dalam kasus ini, saya memiliki tabel besar (mungkin jutaan catatan) dan saya tidak bisa hanya memodifikasi tabel.

Bahkan, kami memiliki kolom tempat kami menyimpan hash data yang tidak boleh digandakan tetapi indeks unik tidak ditetapkan.

Saya mencoba kode java saya untuk memeriksa apakah ada sebelum flush, masih mendapatkan duplikat.

Solusi saya yang mungkin untuk ini adalah:

  • Buat pemicu yang memeriksa apakah hash yang saya coba sisipkan sudah ada di atas meja.
  • Buat tabel lain untuk menyimpan indeks unik untuk tabel ini dan tambahkan kunci asing ke tabel utama.
  • Duduklah di posisi janin dan menangis
rafuru
sumber
Apakah cek hash Anda gagal karena tabrakan hash atau bug di cek?
candied_orange
4
Saya tidak mendapatkan pertanyaan Anda. Jadi, alih-alih mengindeks sekali untuk semua tabel besar Anda dengan jutaan catatan, Anda lebih suka membaca untuk masing-masing juta catatan berikutnya yang akan Anda tambahkan, jutaan yang ada untuk mencari ganda? atau menduplikasi beberapa informasi dan menambahkan bergabung untuk melakukan pemeriksaan Anda?
Christophe
Masalahnya adalah, untuk melakukan perubahan ini, saya telah diperingatkan bahwa kami membutuhkan banyak ruang dan waktu henti yang lama untuk layanan kami, untuk menyelesaikan beberapa persyaratan, layanan kami tidak dapat dimatikan lebih dari 2 jam setiap bulan. Saya tahu cara terbaik adalah dengan melakukan pemeliharaan pada tabel ini, tetapi adalah sesuatu yang tidak bisa saya lakukan saat ini, jadi kami membutuhkan solusi.
rafuru
4
Saya tidak mengerti - mengapa menambahkan pemicu atau menambahkan tabel lain untuk "meniru" indeks membutuhkan waktu henti yang lebih sedikit daripada hanya menambahkan indeks ke tabel yang ada?
Doc Brown
2
@rafuru: siapa bilang Anda perlu membuat indeks unik? Indeks standar dan tidak unik mungkin akan Anda butuhkan untuk dengan cepat menemukan semua baris dengan nilai hash yang sama.
Doc Brown

Jawaban:

3

Ada beberapa skenario yang mungkin mudah dipecahkan, dan yang buruk tidak.

Untuk pengguna yang memasukkan nilai, kemudian masukkan nilai yang sama beberapa waktu kemudian SELECT sederhana sebelum INSERT akan mendeteksi masalah. Ini berfungsi untuk kasus di mana satu pengguna mengirimkan nilai dan beberapa waktu kemudian pengguna lain mengirimkan nilai yang sama.

Jika pengguna mengirimkan daftar nilai dengan duplikat - katakan {ABC, DEF, ABC} - dalam satu permintaan tunggal dari kode aplikasi dapat mendeteksi dan memfilter duplikat, mungkin melemparkan kesalahan. Anda juga perlu memeriksa bahwa DB tidak mengandung nilai unik apa pun sebelum disisipkan.

Skenario yang rumit adalah ketika menulis satu pengguna di dalam DBMS pada saat yang sama dengan menulis pengguna lain, dan mereka menulis nilai yang sama. Maka Anda memiliki ras kondisi di antara mereka. Karena DBMS (kemungkinan besar - Anda tidak mengatakan yang mana yang Anda gunakan) sistem multitasking preemptive tugas apa pun dapat dijeda pada setiap saat dalam pelaksanaannya. Itu berarti tugas pengguna1 dapat memeriksa tidak ada baris yang ada, maka tugas pengguna2 dapat memeriksa tidak ada baris yang ada, maka tugas pengguna1 dapat menyisipkan baris itu, maka tugas pengguna2 dapat memasukkan baris itu. Pada setiap titik, tugas-tugas secara individual bahagia karena mereka melakukan hal yang benar. Namun, secara global terjadi kesalahan.

Biasanya DBMS akan menangani ini dengan mengunci nilai yang dimaksud. Dalam masalah ini Anda membuat baris baru sehingga tidak ada apa pun untuk dikunci. Jawabannya adalah kunci rentang. Seperti yang disarankan ini mengunci berbagai nilai, apakah mereka saat ini ada atau tidak. Setelah dikunci rentang itu tidak dapat diakses oleh tugas lain sampai kunci dilepaskan. Untuk mendapatkan kunci rentang, Anda harus menentukan dan tingkat isolasi SERIALIZABLE . Fenomena tugas lain menyelinap berturut-turut setelah tugas Anda diperiksa dikenal sebagai catatan hantu .

Mengatur level isolasi ke Serializable di seluruh aplikasi akan memiliki implikasi. Throughput akan berkurang. Kondisi balapan lain yang bekerja cukup baik di masa lalu mungkin mulai menunjukkan kesalahan sekarang. Saya akan menyarankan pengaturan pada koneksi yang mengeksekusi duplikat-kode yang menyebabkan Anda dan meninggalkan sisa aplikasi apa adanya.

Alternatif berbasis kode adalah memeriksa setelah penulisan daripada sebelumnya. Jadi lakukan INSERT, lalu hitung jumlah baris yang memiliki nilai hash. Jika ada duplikat kembalikan tindakan. Ini dapat memiliki beberapa hasil buruk. Katakan tugas 1 tulis lalu tugas 2. Lalu tugas 1 memeriksa dan menemukan duplikat. Itu berputar kembali meskipun itu yang pertama. Demikian pula kedua tugas dapat mendeteksi duplikat dan keduanya rollback. Tetapi setidaknya Anda akan memiliki pesan untuk dikerjakan, mekanisme coba lagi dan tidak ada duplikat baru. Rollback disukai, seperti menggunakan pengecualian untuk mengontrol aliran program. Perhatikan baik-baik itu semuapekerjaan dalam transaksi akan dibatalkan, bukan hanya penulisan duplikat. Dan Anda harus memiliki transaksi eksplisit yang dapat mengurangi konkurensi. Pemeriksaan duplikat akan sangat lambat kecuali Anda memiliki indeks pada hash. Jika Anda melakukannya, Anda bisa membuatnya menjadi unik!

Seperti yang Anda komentari, solusi nyata adalah indeks unik. Menurut saya ini harus sesuai dengan jendela perawatan Anda (meskipun tentu saja Anda tahu sistem Anda yang terbaik). Katakanlah hash adalah delapan byte. Untuk seratus juta baris itu sekitar 1GB. Pengalaman menunjukkan sedikit perangkat keras yang masuk akal akan memproses banyak baris ini dalam satu atau dua menit, puncak. Pemeriksaan dan penghapusan duplikat akan menambah ini, tetapi dapat ditulis terlebih dahulu. Ini hanya samping.

Michael Green
sumber
2

Bahkan, kami memiliki kolom tempat kami menyimpan hash data yang tidak boleh digandakan tetapi indeks unik tidak ditetapkan.

Memeriksa tabrakan hash adalah langkah pertama yang baik, tetapi berhati-hatilah, Anda tidak dapat menjamin program yang sama akan menghasilkan hash yang sama pada data yang sama jika di-restart . Banyak fungsi hash "cepat" menggunakan prng inbuilt yang diunggulkan pada waktu mulai program. Gunakan hash kriptografi jika hash harus selalu sama apa pun yang terjadi, seperti yang Anda lakukan dalam aplikasi ini. Perhatikan bahwa Anda tidak memerlukan hash kriptografi yang baik atau aman.

Langkah kedua adalah benar-benar memeriksa kesetaraan data, karena bahkan fungsi hash terbaik pun terkadang akan menghasilkan tabrakan, karena Anda (biasanya) mengurangi entropi data Anda.

Begitu:

Langkah 1: periksa apakah Anda mendapatkan tabrakan pada hash kriptografi

Langkah 2: jika hash cocok, periksa data yang sebenarnya sama

Turksarama
sumber
Saya gagal melihat bagaimana ini menjawab pertanyaan. Mari kita asumsikan sejenak kolom hash yang tersedia diisi oleh fungsi hash deterministik (jika tidak ada upaya untuk menggunakannya tidak masuk akal). Menurut pemahaman saya, masalahnya adalah tidak ada indeks pada kolom hash di database, jadi bahkan langkah pertama dalam jawaban Anda - memeriksa apakah ada tabrakan - masih akan memerlukan pemindaian tabel penuh untuk setiap catatan baru di atas meja dengan beberapa juta rekaman, yang mungkin akan menjadi terlalu lambat.
Doc Brown
Ini adalah yang terbaik yang dapat Anda lakukan tanpa membuat indeks, yang merupakan pertanyaan yang diajukan. Pemindaian hash setidaknya berarti Anda hanya perlu memeriksa satu kolom, yang jauh lebih cepat daripada memeriksa berapa banyak kolom yang harus mereka periksa.
Turksarama
Saya cukup yakin, bahkan ketika membuat indeks tidak mungkin (yang dalam hal ini mungkin adalah), saran asli OPs untuk " membuat tabel lain untuk menyimpan indeks unik untuk tabel ini dan menambahkan kunci asing ke tabel utama " menghasilkan banyak lebih masuk akal.
Doc Brown
Hash deterministik dan kriptografi adalah dua konsep ortogonal bukan? hash kriptografi mungkin tidak deterministik dan sebaliknya hash deterministik sangat mungkin tidak memiliki kekuatan kriptografi.
Newtopian
Mereka bukan hal yang sama, tetapi mereka juga tidak ortogonal. Hash kriptografi adalah bagian dari hash deterministik, tetapi tidak ada yang benar-benar mengganggu membuat hash deterministik non kriptografis kecuali Anda secara khusus ingin hash reversibel karena beberapa alasan.
Turksarama
2

Buat tabel baru dengan kunci primer unik

Di sisi klien mulai membuat GUID untuk setiap catatan sehingga Anda dapat mendeteksi pengiriman ulang sederhana.

Masukkan catatan baru ke dalam tabel baru sehingga setidaknya Anda bagus untuk data baru yang masuk.

Memiliki kolom di tabel baru "CheckedAgainstOldData"

Memiliki tugas backend yang melakukan apa pun yang Anda lakukan saat ini untuk memeriksa hash lambat untuk melihat apakah ia dapat menemukan duplikat dalam data lama dan mengatur bendera yang sesuai, menolak duplikat pada saat ini, mengirim pemberitahuan kembali ke klien.

Sementara itu memiliki tugas backend lain yang memindahkan data dari yang lama ke tabel baru, memeriksa duplikat dengan cek hash Anda dan menghasilkan GUID.

Anda dapat membiarkan tugas ini berjalan selama beberapa hari (jika diperlukan), mentransfer data tanpa downtime.

Setelah transfer selesai, Anda dapat mematikan proses "CheckedAgainstOldData" yang lambat. dan mentransfer semua data ke satu tabel.

Terus terang jika masalahnya seburuk yang Anda gambarkan dan perangkat lunaknya sudah tua, maka Anda akan memiliki ribuan duplikat.

Ewan
sumber
1

Dengan asumsi bahwa data yang berasal dari "pengguna" berarti seseorang yang duduk di keyboard dan bahwa dupes muncul dari dua pengguna yang memasukkan data yang sama pada saat yang sama. Coba tambahkan fungsi yang menyebabkan penundaan acak di awal pemicu. Berikan minimal berapa lama yang dibutuhkan untuk menulis catatan baru ke meja dan mungkin maksimum tidak lebih dari nanocentury atau lebih. Dengan begitu ketika Anda mendapatkan permintaan dupe yang pertama harus dilakukan dan pemicu keberadaan harus mengembalikan hasil yang benar. (Klarifikasi: setiap panggilan harus memiliki waktu tunda acak uniknya sendiri, di sepanjang prinsipal yang sama dengan protokol ALOHA )

Gregor y
sumber