Where ile sorgu yapılacak alanlar için index oluşturmaya gayret edin.
E-Ticaret Veri Tabanı Tablosu İçin Performans İle İlgili Basit Soru
- Düzenlendi
- En İyi Yanıtbyhk44 tarafından
byhk44 Hepsini tek tabloda tutmanız performans açısından sorun olmaz. Çekerken sadece ihtiyacınız olan alanları çekmelisiniz. SELECT * yaparsanız bellek problemleri yaşarsınız. Evet, bunları konuşmuştuk, merak edenler için:
https://laravel.gen.tr/d/5098-veritabaninda-1-tabloda-bircok-veri-tutmak-mi-yoksa-farkli-tablolara-bolmek-mi
VARCHAR limiti geçici tablo (temporary table) ya da MEMORY tablo kullanmadığınız sürece performans açısından sorun teşkil etmez. 150 ile 255 arasında performans açısından fark yok diyebilirsiniz. (Geçici tablo ve MEMORY tabloları byte olarak değil size/limit olarak işlem yapıyor o yüzden onlarda performans sorunu yaşatır.)
1/0 ya da true/false alanlar için uygun olan TINYINT(1)' dir (unsigned olup olmaması önemli değil) ki zaten $table->boolean()
TINYINT(1) şeklinde oluşturur. TINYINT 1 byte; INT ise 4-byte yer kaplıyor ama büyük bir etkisinin olacağını sanmıyorum. Yani 0/1 tutarsanız ve 10 milyon kayıt olsa, TINYINT(1) kullanırsanız 10 mb; INT kullanırsanız 40 mb yer kaplar, 10 bin kayıt olsa 10 kb vs 40 kb olacak...
Veritabanını oluştururken charset ve collation belirlediğinizde oluşan tablo ve içindeki alanlar da aksini belirtmediğiniz sürece aynı charset ve collation ile oluşur o yüzden $table->charset = 'utf8mb4'; ve $table->collation = 'utf8mb4_general_ci'; kısmına ihtiyacınız yok. Ayrıca collation utf8mb4_general_ci değil utf8mb4_unicode_ci olmalı.
Hangi Laravel sürümünü kullanıyorsunuz bilmiyorum ama 7+ kullanıyorsanız:
$table->unsignedBigInteger('tedarikci_id')->nullable();
$table->foreign('tedarikci_id')->references('id')->on('urun_tedarikci');
yerine direkt
$table->foreignId('tedarikci_id')->nullable()->contrained('urun_tedarikci');
yapabilirsiniz. (Ne olursa olsun contrained daima en sona yazılmalı. contrained('urun_tedarikci')->nullable() yapılırsa olmaz)
Çok teşekkür ederim yorumlarınız için,
sineld Çok yeniyim aslında dediğiniz tam olarak ne oluyor ?
mgsmus Sanırım sizde bu konudan bahsettiniz en güzel performanslı şekilde verileri nasıl çekebilirim?
Ürünün kendi sayfasında çok detaylı çekeceğim ama Ürün Listeleme sayfalarında her ürün için 10 ar tane veri çekilebilir.
Bir sorum daha var, diyelim ki tabloyu bigint olarak 20 li oluşturduk ama içerisinde 4 karakter tutuyor buda çok problem olmuyordur değil mi bigint 20 kadar yer kaplamaz int4 kadar kaplar diye tahmin ediyorum doğru mudur?
"geçici tablo (temporary table) ya da MEMORY tablo kullanmadığınız sürece"
Bu konular hakkında detaylı bilgiyi nasıl toplayabilirim?
Eğer tabloları bölersem muhtemelen kod yazma kısmında zorlanacağım bu şekilde tek olarak devam etmem sanırım mantıklı olacak sizinde yönlendirmeleriniz üzerine.
Laravel 8 Kullanıyorum
byhk44 Öncelikle veritabanı indexleme konusunu araştıracaksınız. Forumda da aratabilirsiniz ama bu kapsamlı bir konu, çalışıp öğrenmeniz gerekiyor:
https://dev.mysql.com/doc/refman/8.0/en/optimization-indexes.html
https://laravel.gen.tr/d/4136-database-index
https://laravel.gen.tr/d/3243-pivot-table-sorunu
https://laravel.gen.tr/d/4704-pivot-tabloda-tekrar-eden-kayitlari-nasil-engelleriz
byhk44 Bir sorum daha var, diyelim ki tabloyu bigint olarak 20 li oluşturduk ama içerisinde 4 karakter tutuyor buda çok problem olmuyordur değil mi bigint 20 kadar yer kaplamaz int4 kadar kaplar diye tahmin ediyorum doğru mudur?
https://dev.mysql.com/doc/refman/8.0/en/integer-types.html
byhk44 “geçici tablo (temporary table) ya da MEMORY tablo kullanmadığınız sürece”
Bu konular hakkında detaylı bilgiyi nasıl toplayabilirim?
https://www.mysqltutorial.org/mysql-temporary-table/
https://dev.mysql.com/doc/refman/8.0/en/memory-storage-engine.html
Açıkçası indekslemeyi daha önce duymadıysanız veritabanı konusunda öğrenmeniz gereken çok şey var demektir.
Index'i, kitaplardaki içindekiler kısmı gibi düşünebilirsiniz.
Ahmed Arif'in Hasretinden Prangalar Eskittim şiir kitabından, Uy Havar isimli şiiri okumak için bütün sayfaları tek tek dolaşmaz, kitabın sonundaki içindekilere bakar ve 63. sayfayı açarsınız.
Veritabanı ile bu tür kompleks bir işlem yapacaksanız index konusunu çok iyi bilmeniz gerekir.
Sevgili mgsmus bilmeniz ve dikkat etmeniz gereken birçok noktaya temas etmiş, bunların tamamına dikkat etmezseniz ilerde çok ciddi sıkıntılar çekersiniz.
Rest apide bunu kaynak olarak verdiğinizi varsayarsak bu kadar alanı client attığı isteğinde hepsini kullanmayacaktır o yüzden burada ya grapqQL gibi bir yapı kullandırtın clienta yada client ne istiyorsa siz ozel olarak donun.Gereksiz her data sizden gidecektir.O yüzden yük altında zorlanacaktır sisteminiz.
Bir diğer dezavantajı human readable dedikleri şey..siz okuyamıcaksınız hızlıca.Zaman kaybı yaratacaktır size.
Her ne kadar ilişkilere bölünmesini soylemek istesemde..gereksiz ilişkide kötüdür.Bir orta yolunu bulmalısınız.
Onun haricinde @sineld hocamın dediği gibi where yan tümceleriniz doğru indexleri kullanması önemlidir.
Ornegin @mgsmus tan öğrenmiştim bende bunu paginate() methodu yerine simplePaginate() kullanmanız daha iyi gibi durur bazı durumlarda...
Dediğim gibi human readable yonunden kotu bir tablo ama iyi yonetiyorsanız cok sorun cıkarmaz.
Ancak resim alanlarınızı ben olsam ayırırdım.
Ben olsam;
- Fiyatlar
- Sipariş Miktarları
- Ölçüler
- Özel Alanlar
- Resimler
- Seo
- Durumlar
Diye parçalara bölerdim.
Herkese çok teşekkür ederim, çok faydalı bir konu oldu yine..
İyi çalışmalar dilerim
Hocam verdiğiniz dökümanları incelemek üzere kaydettim başladımda incelemeye, ama tabiki direk burada sizden aldığımız cevaplar gibisi gelmeyecek.
İndex konusunu
https://laravel.gen.tr/d/4136-database-index
Gayet net anladım. Ama bunu laravel 8 de controller kısmında nasıl kullanabiliriz
https://laravel.com/docs/8.x/migrations#indexes
Laravel dökümanlarından inceliyorum ama en sağlıklı bilgiyi yine sizden öğrenmek daha iyi olacak.
Daha önceden bahsettiğim bir haber sistemim vardı orada şuan için kullanacağım ama hangi alanlarda kullanmam gerektiğini tam olarak idrak edemedim.
Mesela bunları haber kategorilerine göre mi yapmalıyız eğer böyle olacaksa bir kategori diğerine göre çok fazla içerik barındırabilir bunuda bölmek gerekecek. Bunu yaparken birde pluck gibi bağlantılar kullanarak indexleri ayrı ayrı mı oluşturacağız burası çok karıştı eğer müsaitseniz basit örnekler ile verebilirseniz çok memnun olurum
Çünkü haberler kategoriler dışarısında çok fazla birbirinden ayrılmıyor kendi içlerinde şu tabloları tutuyor sizce ne yapılmalı?
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_general_ci';
$table->bigIncrements('id');
$table->unsignedBigInteger('yazar_id');
$table->string('baslik');
$table->string('kisa_baslik');
$table->text('ozet');
$table->string('url');
$table->text('icerik');
$table->text('etiket')->nullable();
$table->string('resim_1')->nullable();
$table->string('video')->nullable();
$table->string('seo_baslik')->nullable();
$table->string('seo_aciklama')->nullable();
$table->string('seo_kelime')->nullable();
$table->integer('durum')->default(1);
$table->integer('durum_yorum')->default(1);
$table->integer('durum_reklam')->default(1);
$table->integer('durum_alan1')->default(0);
$table->integer('alan1_siralama')->default(1);
$table->integer('durum_alan2')->default(0);
$table->integer('alan2_siralama')->default(1);
$table->integer('durum_alan3')->default(0);
$table->integer('alan3_siralama')->default(1);
$table->integer('durum_alan4')->default(0);
$table->integer('alan4_siralama')->default(1);
$table->integer('durum_alan5')->default(0);
$table->integer('alan5_siralama')->default(1);
$table->integer('durum_alan6')->default(0);
$table->integer('alan6_siralama')->default(1);
$table->timestamps();
$table->foreign('yazar_id')
->references('id')
->on('haber_yazar');
Bu arada burada verdiğim kodu henüz sizin tavsiyelerinize göre güncellemedim. Bunlarıda güncelleyeceğim birazdan
$table->index([
'baslik',
'kisa_baslik',
], 'index_ismi');
Şeklinde index oluşturursunuz; dizi içinde index alanlarının isimlerini yazarsınız. index_ismi kısmının benzersiz olmasına dikkat etmelisiniz. opsiyoneldir ancak çok sayıda index olması durumunda sorun yaşayacağınızdan elle tanımlanmasını öneririm.
- Düzenlendi
sineld Bu dediğinizi migrations'da oluştururken yapıyoruz değil mi?
Peki controller'da verileri çekerken ayrı bir şey yapmamıza gerek kalmıyor değil mi? Öyleyse normal çektiğimiz gibi çekiyoruz verileri diye anladım
Bu basit olacak ama bu örnek için ;
`
public function index()
{
$data = Haber::with('haberKategoriler')
->latest('created_at')
->get();
return view ('SS.haber.index',compact('data'));
}`
Burada adminde tüm verileri çektik, ama detay sayfalarında da mantık olarak bu şekillerde yazacağız herhalde (paginate) v.s. alanlarını henüz hazırlamadım o yüzden çok boş kaldı)
Bu arada
$table->index([
'baslik',
'kisa_baslik',
], 'index_ismi');
integer, boolean, string gibi alanlar tanımlamıyor muyuz bu alanlarda?
Text dışındaki alanları ekleyebiliyorsunuz. text alanlar için farklı bir index yapısı mevcut, onunla da ilgili örnek verebilirim.
Migrations içinde yapıyorsunuz, controller vs her yerde standart kullanıyorsunuz, ek bir şey yapmanıza gerek yok.
- Düzenlendi
sineld Peki bunu nasıl yapacağız?
$table->index([
string('baslik'),
integer('kisa_baslik'),
], 'index_ismi');
Gibimi olacak acaba?
Eğer text içinde örnek verebilirseniz çok sevinirim.
mgsmus Hocam burada konuşulanları benimle paylaştığınız diğer konulara eklememiz gerek diğer faydalanacak kişiler için çok açıklayıcı oldu bu konu.
Aslında sitede böyle bir yer olsa çok lezzetli olur top soruların olduğu alan herkesin çok işine yarar.
Çok teşekkür ederim ilgi alakanız için hocam.
- Düzenlendi
sineld Sanırım şimdi çözdüm
İndexleride aşağıda ayriyetten veriyoruz sanırım. Foreign key gibi.
{
Schema::create('haber_deneme', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('baslik');
$table->string('ozet');
$table->string('url');
$table->string('aciklama');
$table->index([
'baslik',
'ozet',
], 'haberindex1');
});
}
Bunu yaptık ve tüm olay halloldu sanırım? Yani bundan sonra laravel bizim için controller'da haberi çekerken sadece where ile başlık ve özeti çekeceksek mysql de index eklenmiş olduğunu görecek ve farklı bir koda gerek kalmadan daha az yorulacak ve performansımız katlanacak, birde bunları yaptıktan sonra farklı tablolara bölmeye gerek kalmıyormu tabi bu çok göreceli bir kavram ama ayrı ayrı uğraşmak yerine index yapılırsa performansta ayrı ayrı yapmak gibi artacak diye düşünüyorum doğru mudur?
Burada ön tarafta haber sitesinde anasayfa'da sadece haberin başlığı ve özetini çekeceksek böyle bir örnek yapmanız mümkün mü en doğrusu nasıl olacaktır?
Hepsi bu kadar, yapacağın başka bir şey yok.
- Düzenlendi
byhk44 Başlık, özet, içerik... gibi içinde arama yapılacak alanlara düz index atılmaz. Onlarda full-text index kullanılır çünkü onlarda bir şey bulmaya çalışsanız = değil LIKE kullanırsınız ama LIKE index kullanamadığı için yerine full-text index kullanılır. Bunun gibi birçok konu var o yüzden index konusu sizin araştırmanız gereken bir konu. Forumdan ziyade, bu işi öğrenmek istiyorsanız kendi araştırmanızı yaparak ilerlemeniz doğru olur. Forumda, ilerledikten sonra gelip takıldığınız yerleri sorabilirsiniz. Şu aşamada sorduğunuz soruların çoğunun cevabı için kitap yazıyorlar.
Laravel değil SQL düzeyinde nasıl yapılıyor, ne ifade ediliyor onu öğrenin. Bu konu Laravel ya da PHP değil veritabanı konusu.
Baslık ve ozet icin niye index attınız ki..arama mı yaptırcaksınız.
Yalnış bir yol bence.uygulamanız ne kadar büyücek bilmem ama.Tablo büyüdükce indexleriniz de bir işe yaramaz.Hele ki LIKE yapacaksanız full-text-index kullanmanız lazım.Ama bana kalırsa database de arama yapmayın.Bunun icin elasticsearch gibi 3.parti uygulamaları kullanın.
- Düzenlendi
Şimdi anladım, arama yapmak dediğiniz search bar üzerinden bir arama yapmak, yani anasayfada içerikleri gösterirken yapıyoruz sandım.
Şu şekilde bir veri için 20 tane sütun var ama ana sayfada sadece 3 tane görülecek başlık özet açıklama diyelim.
Bunlara index yapmaya gerek yokmu ?
Sadece haber ararkan öyleyse index kullanılacak diye şuan anladım.
Burada kafam karıştı son sorum bu olsun
İndex arama yapılan kısımlar için mi kullanılıyor yoksa 20 sütunlu bir verinin 3 parçasını anasayfada veya kategori sayfalarında gösterirken mi index lazım
Yani şurası için index gerekmiyor mu? bu gösterdiğim kategori sayfası paginate kullanılarak belkide 500 sayfa içerisinde 10.000 veri olacak ileride diye düşünebiliriz
@foreach($haber as $data)
<div class="content-entry-wrap dzn-1">
<div class="entry-content">
<h3 class="entry-title fw-600">
<a href="{{$data->url}}">{{$data->baslik}}</a>
</h3>
</div>
<div class="entry-summary">
<p>{{$data->ozet}}</p>
</div>
</div>
@endforeach
mgsmus teşekkür ederim hocam dediğiniz gibi sayfalarca bilgiyi tek yerde aramaya kalkıyoruz doğru bilgiye ulaşamama yada doğru bilgiyi akılda doğru sentezleyememe korkusundan direk sizlerden bilgi almak çok faydalı oluyor.
- Düzenlendi
byhk44 Hayır anlamadınız. Bir tane haberler diye tablonuz olsun:
CREATE TABLE news
(
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) not null,
slug VARCHAR(255) not null,
content LONGTEXT not null
);
Buraya iki tane index atılacak, biri slug için unique index, diğeri title ve content için full-text (id MySQL'de otomatik olarak PRMARY index olur). Slug benzersiz olması gerektiği için index unique olacak böylece hem aynı slugdan eklenmesini veritabanı düzeyinde engellemiş olacağız hem de WHERE slug = 'haber-basligi'
gibi sorgular hızlı çalışacak:
CREATE UNIQUE INDEX news_slug_unique_index on news (slug);
title ve content alanı için ise full-text index:
CREATE FULLTEXT INDEX news_search_index ON news (title, content);
Bu indexi kullanabilmek için full-text araması yapılması lazım:
SELECT *,
MATCH(title, content) AGAINST('"arama+ kelimesi+"' IN BOOLEAN MODE) as score
FROM news
WHERE MATCH(title, content) AGAINST('"arama+ kelimesi+"' IN BOOLEAN MODE)
ORDER BY score
Örneğin şimdi bir LIKE sorgusunu bir de MATCH...AGAINST sorgusunu EXPLAIN ANALYZE ile kontrol edelim:
EXPLAIN ANALYZE
SELECT *
FROM news
WHERE title LIKE '%arama kelimesi%'
OR content LIKE '%arama kelimesi%';
EXPLAIN ANALYZE
SELECT *,
MATCH(title, content) AGAINST('"arama+ kelimesi+"' IN BOOLEAN MODE) as score
FROM news
WHERE MATCH(title, content) AGAINST('"arama+ kelimesi+"' IN BOOLEAN MODE)
ORDER BY score DESC
İlk sorgunun çıktısı:
-> Filter: ((news.title like '%arama kelimesi%') or (news.content like '%arama kelimesi%')) (cost=0.35 rows=1) (actual time=0.016..0.016 rows=0 loops=1)
-> Table scan on news (cost=0.35 rows=1) (actual time=0.015..0.015 rows=0 loops=1)
İkinci sorgunun çıktısı
-> Sort row IDs: (match news.title,news.content against ('"arama+ kelimesi+"' in boolean mode)) (cost=0.35 rows=1) (actual time=0.009..0.009 rows=0 loops=1)
-> Filter: (match news.title,news.content against ('"arama+ kelimesi+"' in boolean mode)) (actual time=0.005..0.005 rows=0 loops=1)
-> Indexed full text search on news using news_search_index (title='"arama+ kelimesi+"') (actual time=0.004..0.004 rows=0 loops=1)
İkinci sorguda Indexed full text search on news using news_search_index... şeklindeki bize sorguyu çalıştırırken index kullandığını belirtiyor. İkinci sorguda ise Table scan on news... diyerek index kullanmadığını, tüm tabloyu taradığını belirtiyor. Boş tablo olmasına rağmen ilk sorgunun süresi 0.015 ms iken ikinci sorgunun süresi 0.004 ms yani ilk sorgu ikinci indexli sorgunun 3.75 katı daha yavaş...
Primary index, tarihlerde index kullanımı, composite index, partial composite index, B-TREE, B+TREE, HASH... Şimdi anlatabilmiş olmam lazım. Bu konu PHP ya da Laravel ile ilgili bir konu değil...
- Düzenlendi
mgsmus Hocam çok teşekkür ederim iyice karıştı ortalık ama bir şekilde anlayıp uygulamaya çalışacağım elinize emeğinize sağlık.. "İndex arama yapılan kısımlar için mi kullanılıyor yoksa 20 sütunlu bir verinin 3 parçasını anasayfada veya kategori sayfalarında gösterirken mi index lazım" yani tam olarak her veri çekme işlemi aslında bir arama yapmakmıdır? bunun tam olarak cevabı nedir burayıda anlarsam hepsini harmanlayacağım sanırım
Bu arada benim hatam tam olarak öğrenmeden bir uygulamaya başlamak oldu, ama muhtemelen birşeyleri denemeden ve uygulamadan öğrenmek çok zor olacaktı. Bu şekilde karışık gitmek daha faydalı gibi geliyor bana