Sqlite3worker untuk Multi Threading pada Database Sqlite

By | February 19, 2024
Print Friendly, PDF & Email
1,101 Views

multi threading update records in Sqlite3 Engine – Postingan berikut merupakan masalah dan solusi yang saya kerjakan ketika mengalami error pada Multi Threading di Sqlite, dimana secara default Sqlite merupakan datasabe single user dan Operation dalam satu waktu.

Kebutuhan operasi pararel sangat dibutuhkan untuk mempercepat operasi kerja tanpa menunggu operasi sebelumnya selesai sehingga waktu tidak terbuang percuma. Bisa dibayangkan 1 operasi kerja butuh waktu 0.5 detik untuk selesaikan operasi kerjanya, maka hanya 2 operasi kerja yang bisa dicapai dalam waktu 1 detik!

Bisa dibayangkan butuh jutaan operasi kerja tanpa pararel maka akan sangat lama selesainya bukan? Dengan multi threading di Python yang memudahkan bekerja pararel maka bisa kita tingkatkan 1 detik menjadi puluhan/ratusan operasi kerja!

SQLite adalah sebuah pustaka perpustakaan C yang menyediakan basis data SQL kecil, cepat, swasembada, tingkat tinggi, dan berdokumen penuh yang diakses menggunakan bahasa pemrograman Python. Dalam Python, Anda dapat menggunakan modul bawaan sqlite3 untuk berinteraksi dengan database SQLite. Modul ini menyediakan antarmuka Python untuk bekerja dengan database SQLite tanpa memerlukan instalasi perangkat lunak tambahan.

Saya banyak menggunakan Sqlite3 untuk menyimpan data terutama dalam manajemen data dalam bentuk tabel daripada menggunakan CSV mengingat akan ada operasi update pada record tersebut. Misalkan yang saya alami untuk mengelola data dari kombinasi password rockyou https://github.com/praetorian-inc/Hob0Rules/blob/master/wordlists/rockyou.txt.gz yang terdiri dari 1,4 Juta record.

CSV to Sqlite Database

Saya akan buat class DB() yang berguna untuk operasi kerja CRUID di SQLite yang sekalian jadi tutorial bagi kalian. Mari kita lakukan extract dan loading via pandas seperti berikut dan jangan lupa droping na

See also  Library SQLite untuk Menyimpan Jutaan record
import pandas as pd
passwords = pd.read_csv('rockyou.txt',header=None,on_bad_lines='skip',encoding = "ISO-8859-1")
passwords.dropna(inplace=True)

Membuat database SQlite

Adapun langkah selanjut nya yaitu memasukannya ke dalam record table

import sqlite3
from sqlite3 import Error

class DB():
    def __init__(self,db_file):
        self.db_file = db_file
        self.conn =  None
        self.sql_buat_tabel = """ CREATE TABLE IF NOT EXISTS password (
                                            id integer PRIMARY KEY,
                                            pwd text NOT NULL,
                                            keterangan text
                                        ); """
        self.buka()
        self.buat_tabel()

    def buka(self):
        try:
            self.conn = sqlite3.connect(self.db_file)
            return self.conn
        except Error as e:
            print(e)
        return self.conn
    def buat_tabel(self):
        try:
            c = self.conn.cursor()
            c.execute(self.sql_buat_tabel)
        except Error as e:
            print(e)
    def masukan_data(self,info):        
        sql = ''' INSERT INTO password(pwd,keterangan)
                  VALUES(?,?) '''
        cur = self.conn.cursor()
        cur.execute(sql, info)
        #self.conn.commit()
    def get(self):
        cur = self.conn.cursor()
        cur.execute("SELECT * FROM password")
        results = pd.DataFrame.from_records(data = cur.fetchall(),columns=['id','pwd','keterangan'])
        return results
    
    def update_data(self,info):
        sql = ''' UPDATE password 
                 SET keterangan = ? WHERE id = ?  '''
                
        cur = self.conn.cursor()
        cur.execute(sql, info)
        self.conn.commit()

Oiya untuk commit() nya saya off kan, karena akan disesuaikan dengan kebutuhan

def masukan_data(self,info):        
    sql = ''' INSERT INTO password(pwd,keterangan)
              VALUES(?,?) '''
    cur = self.conn.cursor()
    cur.execute(sql, info)
    #self.conn.commit()

kemudian kita akan memasukannya  nya dengan aturan panjang minimal karakter 6 dan akan commit kalau sudah 1000 record

db = DB('rockyou.sqlite')
for i,pwd in enumerate(passwords[0]):
    #password minimal panjang 6 karakter
    if len(pwd)>=6:
        data = (pwd,'')
        db.masukan_data(data)
    if (i%1000)==0:
        db.conn.commit()
        print("commit: ",i)

Sebenarya untuk insert data paling gampang dan cepet, bisa kalian lakukan Import CSV via Belajar Database Relational SQL Lite Bagian 3  DB Browser Sqlite

Selanjutnya, kalian bisa lihat function dari update_date() yang merupakan update dan langsung panggil commit()

def update_data(self,info):
    sql = ''' UPDATE password 
             SET keterangan = ? WHERE id = ?  '''
            
    cur = self.conn.cursor()
    cur.execute(sql, info)
    self.conn.commit()

Update Record menggunakan Proses Threading di Sqlite

Inilah yang jadi panggal permasalahan, ketika saya akan melakukan update record menggunakan proses threading di Sqlite ternyata ada pesan error. Berikut ketika saya akan memanggil sebuah function dummy_hack() dan autologin() –> sengaja saya tidak lengkapi detail

def dummy_hack(db,record):
    pwd = record['pwd']
    index = record['id']
    #code hack dibawah ini
    sukses = autologin('@username',pwd)    
    #selesai
    record_update = ('sudah dicek',int(index))
    db.update_data(record_update)    
    if sukses:
        stopall()

Kemudian kita panggil multi threading biar proses nya pararel.

import time
import os
import threading
import pandas as pd

results = db.get()
#pakai pandas buat query --> 
#buat pisahkan record yang belum dicek/digunakan
uncek = results.query("keterangan!='sudah dicek'")
uncek.reset_index(inplace=True)

kemudian lakukan looping untuk setiap record dan lakukan threading!

for i in range(0,len(uncek)):
    if os.path.exists('stop.txt'):
        break
    record = uncek.loc[i]
    t = threading.Thread(target = dummy_hack, args = (db,record))
    t.start()
    # t.join()
    time.sleep(0.5)

dan apa yang terjadi!

ProgrammingError: SQLite objects created in a thread can only be used in that same thread.The object was created in thread id 23508 and this is thread id 22640

Namanya juga Sqlite yang cocok untuk single user dan single connection! maka ketika ada multiple operation seperti update/insert/delete diatas dalam waktu yang bersamaan tentu akan error!

See also  Belajar Database Relational SQL Lite Bagian 4 - Join Table

Solusi yang rumit bisa kalian baca dokumentasi berikut yaitu Threading Safe Mode

https://www.sqlite.org/threadsafe.html

Tapi males baca, euy

Sqlite3worker untuk Multi Threading pada Database Sqlite

Solusi paling gampang yaitu cari library saja dan ketemulah solusi menarik dari https://github.com/dashawn888/sqlite3worker, tentu saya buat class DB menjadi DB_Worker dengan tugas hanya update dan get saja.

class DB_Worker():
    def __init__(self,db_file):
        self.db_file = db_file
        self.worker = Sqlite3Worker(self.db_file)
    def get(self):
        result = self.worker.execute("SELECT * FROM password")
        result = pd.DataFrame(result,columns=['id','pwd','keterangan'])        
        return result
    def update_data(self,data):
        self.worker.execute("UPDATE password SET keterangan = ? WHERE id = ?", data)        

Kita panggil ulang lagi class DB_Worker()

db = DB_Worker('rockyou.sqlite')
results = db.get()
    
uncek = results.query("keterangan!='cek'")
uncek.reset_index(inplace=True)

dan tentunya lakukan multi threading tanpa masalah!

for i in range(0,len(uncek)):
    if os.path.exists('stop.txt'):
        break
    record = uncek.loc[i]
    t = threading.Thread(target = dummy_hack, args = (db,record))
    t.start()
    time.sleep(0.5)

db.worker.close()

kalau kurang kenceng! time.sleep(0.001) biar makin ngebut threading nya!

Kesimpulan

Beberapa kerjaan sebelumnya, saya terbiasa menggunakan MySql bila bekerja dengan Database yang mengharuskan Multi Threading maka dengan Sqlite3worker untuk Multi Threading pada Database Sqlite merupakan solusi yang layak kalian gunakan! tanpa perlu ribet!

 

Leave a Reply