WebSql ve SqLite'i Verimli Kullanma, Kullanımı

javascriptsqlitekendimce

2 yıl önce 1 yorum

Merhabalar, Websql veya SqLite çalıştığım şirketteki uygulamalarda ve kendi yaptığım uygulamalarda benim için vazgeçilmez hale gelmiş durumda. Bunu çoğunlukla veri yedekleme ve hızlı erişim için kullanıyorum. Bir diğer adıyla önbellek (cache). Bu konu şu sorulara da cevap verebilir aslında:

sqlite hızlandırma, sqlite performance, sqlite performans, sqlite çok yavaş

Modellerle Çalışmak

Bir chat uygulaması yapıyoruz, uygulamadaki konuşmalar sqlite üzerine yedekleniyor ama sürekli olarak SqLite üzerinden işlem yapmıyoruz. Bir mesajı görüntülemek için SqLite değil, uygulama içinde oluşturduğumuz modelleri kullanıyoruz. Bu bize uygulamanın hızlı işlem yapmasını disk üzerinde okuma yazma işleminin sıfıra inmesini sağlıyor.

Aslında bir MessageList ve Message adında iki adet GLOBAL modellerim var. Örnek gösterecek olursak:

import SqlService from '...';


class Message {
	constructor(msg = {}) {
		this.id = msg.id || new Date().getTime();

		this.text = msg.text || null;
	}
}

class MessageList {
	constructor() {
		this.messageList = [];
	}

	add(message, storage = true) {
		this.messageList.push(message);
		if (storage === true)
			this.syncStorage(message);
	}

	get(id) {
		return this.messageList.find(x => x.id === id);
	}

	syncStorage(message) {
		SqlService.insert(
			'messages',
			Object.keys(message),
			Object.values(message)
		);
	}
}
	

Burada mesaj listesine add methodu ile eklenecek mesaj eğer veritabanına kaydedilmek isteniyorsa syncStorage çağrılıyor. get methodu ile de mesaj listesinden getiriliyor. Peki biz mesaj listesini SqLite üzerinde yönetseydik? Tam bir fiyasko olurdu :) İlk yaptığım uygulamalarda işte bu fiyasko ile çalışıyordum. Yani benim bu konuda anlatmak istediğim şeyler tecrübelerime dayanıyor :).

Disk okuma yazma kullanımı önemliyken RAM kullanımı da çok önemli. Burada kullanıcının 1000 adet konuşması olduğunu düşünün. Hepsini tek seferde çekip ekrana yansıtmak mantıklı mı? değil. İşte burada da kullanıcının konuşmayı ilk açtığında tüm mesajlarını getirmek yerine mesaj listesinden yukarı gittikçe mesajları getirmek daha mantıklı olacaktır.

Transaction Yönetimi

İlk önce transaction tanımında bahsedelim: Sql üzerinde bir string sorguyu çalıştırmamıza izin veren method diyebiliriz. Transaction'ı açmak için ayrı bir method var. Örnek:

var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);

/*
	bunu dediğiniz anda callback'e disk üzerinde işlem yapmak için,
	sorgu çalıştırabileceğiniz, bir değer döndürür.
*/
db.transaction(callback);

/*
	executeSql ise tx ile izin verilen yerde ilk parametresindeki sorgu ile
	ikinci parametresinde bulunan diziyi prepare eder (birbirine bind eder).
	üçüncü parametresinde bu sorgu bitti ise sonucunu, dördüncü parametrede ise
	hatalı bittiğini döndürür. 
*/
db.transaction(function(tx) {
	tx.executeSql('', [], basarili, hata);
});
	

Yani bizim bir sql komutu çalıştırabilmemiz için bir transaction'dan gelen tx değerine ve bu komutu çalıştırabilecek executeSql methoduna ihtiyacımız var. Gayet masraflı bir iş. Şimdi biraz daha örneklendirelim.

Öyle bir insan düşünün ki elinden telefonu bırakmıyor ve sürekli konuşuyor, aynı zamanda ona da mesaj geliyor. Dakikada 10 adet mesaj alan ve atan birisinden bahsediyorum. Bir gün, o kişinin şarjı bittiği için telefonu kapandı ve telefonun yarım saat şarj olmasını bekledi. 30*10 = 300 adet mesaj görülmedi. WOW :) Telefonu açtıııııı. E hadi bakalım sen her mesaj için transaction mı? açacaksın. Yani gidip 300 adet Sql okuma yazma izni isteyeceksin. Bunu bir yazımda bahsettiğim console.time ve console.timeEnd ile örneklendireceğim.

Eğer biz elimizde bulunan 300 adet mesajı sürekli transaction açarak yaparsak:

var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);

// ilk önce tabloyu oluşturalım.
db.transaction(function(tx) {
	tx.executeSql('CREATE TABLE IF NOT EXISTS messages(id TEXT, text TEXT)', []);
});

// sonra 300 adet bilgiyi tek tek transaction açarak ekliyoruz
console.time('hizTesti');
var execSayisi = 0;
for (var i = 0; i < 300; i++) {
	db.transaction(function(tx) {
		tx.executeSql('INSERT INTO messages VALUES (?, ?)', [i, 'Test mesajı'], function(res) {
		execSayisi++;
			if (execSayisi === 300)
				console.timeEnd('hizTesti');

		});
	});
}

// Sonuç:
// hizTesti: 2.25 Saniye
	

Eğer biz elimizde bulunan 300 adet mesajı BİR TEK transaction açarak yaparsak:

var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);

// ilk önce tabloyu oluşturalım.
db.transaction(function(tx) {
	tx.executeSql('CREATE TABLE IF NOT EXISTS messages(id TEXT, text TEXT)', []);
});

// sonra 300 adet bilgiyi bir tek transaction açarak ekliyoruz
console.time('hizTesti');
var execSayisi = 0;
db.transaction(function(tx) {
	for (var i = 0; i < 300; i++) { // dikkat döngü içeride kuruldu
		tx.executeSql('INSERT INTO messages VALUES (?, ?)', [i, 'Test mesajı'], function(res) {
		execSayisi++;
			if (execSayisi === 300)
				console.timeEnd('hizTesti');

		});
	}
});

// Sonuç:
// hizTesti: 37 MiliSaniye
	

Aradaki farkın ne kadar önemli olduğunu farkettiniz değil mi? Her sorgu için transaction açarsak 2.25 saniye, eğer 300 sorgu için bir tane açarsak 0.37 saniye

Bunun önemini düşünürsek kullanıcı uygulamaya girer girmez yaklaşık 3 saniye belki de kasma yaşayacak. Bu düşündüklerimiz en iyi ihtimaller :) Web tarayıcı üzerinden giren kullanıcının bu sorguları kaldırması çok kolay. Bunu bir de mobil platform üzerinde denediğinizde uygulama bazen 5 saniye süreye kadar kasabiliyor.

Bunları otomatik yapan bir şey yok mu? kardeşim

Yine kendi yaptığım bir kütüphaneyi tanıtacağım ama :) ismi WebSqLite. Bu Ağustos 2016 da başlattığım bir proje ve şu an hala üzerinde geliştirmeler yapıyorum. Bu geliştirmeler de Transaction Yönetimi başlığında anlattığım olaylara değiniyor. Bu kütüphanede basit insert, çoklu insert, update, select, delete işlemleri yapabiliyorsunuz. Ve bu kütüphane platform bağımsız ister react native üzerinde kullanın ister tarayıcı üzerinde SqLite olması yeterli.

Bu kütühanede bulunan en önemli özellik saniye bazında transaction açtırabilmesi. Örneğin timeout değerini 5 saniye yaparsanız 5 saniye içinde gönderilen sorguları tek transaction üzerinde işliyor. Select için (diğer sorguların dışında) anında işleme sokuyor.

Mobil bir uygulama yazdığınızda hız gerçekten çok önemli bir duruma geliyor. Tarayıcı mobilde yapılan zorlu işlemleri kaldırabiliyor ama mobil cihazlar 512MB'dan ..GB'ta kadar tasarlanmış ve hepsini düşünmeniz test etmeniz gerekiyor. Bazı telefonlar desteklenmiyor, bazıları birkaç özelliği destekliyor. Bazı telefonlarda zorlu animasyonlar yapmak size pahalıya patlıyor falan :) Aklıma gelen ve sık karşılaştığım sorunları böyle makaleler haline getirmeye devam edeceğim takipte kalın ve lütfen bloguma e-mail ile abone olun :) Makalemi okuduğunuz için teşekkür ederim.

Bugün anlatacaklarım bu kadardı aklınıza takılan bir soru olursa çekinmeyin. Eğer bu konu hakkında veya konu dışı olarak bana soru sormak istiyorsanız https://github.com/abdurrahmanekr/bana-istedigini-sor repository’sinde bir issue açabilirsiniz. Youtube kanalıma abone olarak ve yayınladığım bu makaleyi paylaşarak bana destek olabilirsiniz. Diğer makalelerde görüşmek üzere :)

Yorumlar ({{totalCommentCount}})

  • coder

    {{commentLike130Count}} beğenme 1 ay önce

    Merhaba hocam Ben ingilizce bir oyun yapıcam ve adminden girilen cümleleri ve cevaplarını alıp kullanıcınınkiyle eşleşmesi durumunda doğru ya da yanlış durumuna göre diğer çeviri yapılması istenen cümleelere geçecek öğretici bir oyun olacak web sitesinde html css javascript tabanlı bir içerik olacak Bana bu veritabanını nasıl halledebilirim yardımda bulunursanız bir tavsiyede bulunursanız çok sevinirim. Cevabınızı bekliyor olacağım
    Beğen Beğendin
  • Düşündüklerin nedir ?

    Abdurrahman Eker

    (1010 Eylül 11111001100)

  • Full Stack Developer at Detaysoft Turkey/Sivas
  • İnternette Avare Kodcu
  • github
  • linkedin
  • youtube
  • Yeni içeriklerden haberdar olmak ister misin ?