Tüm Tabloların Kayıt Sayısını Tek Sorguda Görmek

Bir veritabanını yönetirken en sık karşılaştığın durumlardan biri şudur: “Hangi tabloda ne kadar veri var?” sorusu. Özellikle production ortamında onlarca, hatta yüzlerce tablo barındıran bir veritabanında her tabloyu tek tek sorgulamak hem zaman kaybı hem de ciddi bir eziyet. İşte bu noktada MariaDB ve MySQL’in sunduğu sistem tabloları ve dinamik SQL yetenekleri devreye giriyor. Bu yazıda, tüm tablolardaki kayıt sayısını tek sorguda nasıl görebileceğini, farklı yöntemlerin artı ve eksilerini, ve gerçek dünya senaryolarında bunları nasıl kullanacağını detaylıca ele alacağım.

Neden Bu Sorguya İhtiyaç Duyarsın?

Günlük operasyonlarda bu bilgiye ihtiyaç duyduğun onlarca senaryo var. Mesela bir e-ticaret sitesinin veritabanını devraldın. Önceki ekipten sağlıklı bir dokümantasyon gelmiyor. İlk iş olarak “Bu veritabanında ne var, nasıl bir veri yoğunluğu söz konusu?” diye bakmak istersin. Ya da otomatik temizlik scriptleri yazıyorsun ve bir tablonun belirli bir kayıt sayısının altına düşüp düşmediğini kontrol etmen gerekiyor. Bunlara ek olarak, yedekleme stratejisi belirlerken hangi tabloların daha kritik ve daha büyük olduğunu bilmek isteyebilirsin. Tüm bu ihtiyaçlar için “tüm tablolarda kayıt sayısı” sorgusu birebir.

Yöntem 1: information_schema Üzerinden Hızlı Tahmin

MariaDB ve MySQL, information_schema adlı bir sistem veritabanı barındırır. Bu veritabanındaki TABLES view’u, her tablo hakkında metadata bilgisi tutar. Bunlardan biri de TABLE_ROWS kolonudur.

SELECT 
    TABLE_NAME AS 'Tablo Adı',
    TABLE_ROWS AS 'Kayıt Sayısı (Tahmini)',
    ENGINE AS 'Motor',
    DATA_LENGTH / 1024 / 1024 AS 'Veri Boyutu (MB)'
FROM 
    information_schema.TABLES
WHERE 
    TABLE_SCHEMA = 'veritabani_adin'
    AND TABLE_TYPE = 'BASE TABLE'
ORDER BY 
    TABLE_ROWS DESC;

Bu sorguyu çalıştırdığında saniyeler içinde tüm tabloların listesini alırsın. Ancak burada kritik bir uyarı var: InnoDB motorunu kullanan tablolarda TABLE_ROWS değeri gerçek sayıyı değil, yaklaşık bir tahmini gösterir. Bu tahmin, özellikle büyük tablolarda %20-40 sapma gösterebilir. MyISAM tablolarda ise bu değer kesindir çünkü MyISAM kayıt sayısını ayrıca saklar.

Bu yöntemi şu durumlarda kullan:

  • Genel bir fikir edinmek istiyorsan
  • Hız öncelikliyse
  • Kesin sayı kritik değilse
  • Büyük tablolarda bile hızlı sonuç istiyorsan

Yöntem 2: COUNT(*) ile Kesin Sonuç – Dinamik SQL

Eğer kesin kayıt sayısına ihtiyacın varsa, her tablo için COUNT(*) çalıştırman gerekir. Bunu elle yapmak yerine, dinamik SQL üreten bir sorgu yazabiliriz.

Önce hangi tabloların var olduğunu görelim:

SELECT 
    GROUP_CONCAT(
        'SELECT ''', TABLE_NAME, ''' AS tablo_adi, COUNT(*) AS kayit_sayisi FROM `', 
        TABLE_NAME, '`'
        SEPARATOR ' UNION ALL '
    ) AS dinamik_sorgu
FROM 
    information_schema.TABLES
WHERE 
    TABLE_SCHEMA = 'veritabani_adin'
    AND TABLE_TYPE = 'BASE TABLE';

Bu sorgu sana çalıştırabileceğin dinamik bir SQL çıktısı verir. Ama bu yaklaşımı production’da doğrudan kullanmak biraz zahmetli. Daha pratik olan stored procedure yöntemi.

Yöntem 3: Stored Procedure ile Otomatik COUNT(*)

Production ortamlarında en çok tercih ettiğim yöntem budur. Bir kez yazarsın, sonrasında tek komutla çağırırsın:

DELIMITER $$

CREATE PROCEDURE tum_tablolarin_kayit_sayisi(IN db_adi VARCHAR(255))
BEGIN
    DECLARE bitti INT DEFAULT 0;
    DECLARE tablo_adi VARCHAR(255);
    DECLARE sorgu TEXT;
    
    DECLARE tablo_cursor CURSOR FOR
        SELECT TABLE_NAME 
        FROM information_schema.TABLES 
        WHERE TABLE_SCHEMA = db_adi 
        AND TABLE_TYPE = 'BASE TABLE'
        ORDER BY TABLE_NAME;
    
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET bitti = 1;
    
    CREATE TEMPORARY TABLE IF NOT EXISTS kayit_sayilari (
        tablo_adi VARCHAR(255),
        kayit_sayisi BIGINT
    );
    
    TRUNCATE TABLE kayit_sayilari;
    
    OPEN tablo_cursor;
    
    tablo_dongusu: LOOP
        FETCH tablo_cursor INTO tablo_adi;
        IF bitti = 1 THEN
            LEAVE tablo_dongusu;
        END IF;
        
        SET sorgu = CONCAT(
            'INSERT INTO kayit_sayilari SELECT ''', 
            tablo_adi, 
            ''', COUNT(*) FROM `', 
            db_adi, '`.`', 
            tablo_adi, '`'
        );
        
        PREPARE stmt FROM sorgu;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END LOOP;
    
    CLOSE tablo_cursor;
    
    SELECT * FROM kayit_sayilari ORDER BY kayit_sayisi DESC;
    
    DROP TEMPORARY TABLE kayit_sayilari;
END$$

DELIMITER ;

Oluşturduktan sonra şu şekilde çağırırsın:

CALL tum_tablolarin_kayit_sayisi('veritabani_adin');

Bu procedure her tablo için gerçek COUNT(*) yapar, sonuçları geçici bir tabloda toplar ve sana sıralı olarak sunar. Dezavantajı: Büyük tablolarda uzun sürebilir. Milyonlarca kayıt içeren tablolar varsa bu procedure dakikalarca çalışabilir. Production saatlerinde çalıştırmaktan kaçın.

Yöntem 4: Bash Script ile Dışarıdan Toplama

Sistem yöneticisi bakış açısıyla en esnek yöntemlerden biri de bu işi MySQL dışında, bir Bash scriptiyle yapmak. Özellikle sonuçları bir log dosyasına yazmak veya monitoring sistemine göndermek istiyorsan bu yaklaşım çok işe yarıyor:

#!/bin/bash

DB_HOST="localhost"
DB_USER="monitor_user"
DB_PASS="guclu_parola"
DB_NAME="veritabani_adin"
LOG_FILE="/var/log/db_kayit_sayilari.log"
TARIH=$(date '+%Y-%m-%d %H:%M:%S')

echo "=== $TARIH ===" >> "$LOG_FILE"

# Tablo listesini al
TABLOLAR=$(mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" -N -e 
    "SELECT TABLE_NAME FROM information_schema.TABLES 
     WHERE TABLE_SCHEMA='$DB_NAME' AND TABLE_TYPE='BASE TABLE' 
     ORDER BY TABLE_NAME;" 2>/dev/null)

if [ $? -ne 0 ]; then
    echo "HATA: Veritabanina baglanılamadı!" >> "$LOG_FILE"
    exit 1
fi

# Her tablo için COUNT(*) çalıştır
for TABLO in $TABLOLAR; do
    SAYI=$(mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" -N -e 
        "SELECT COUNT(*) FROM `$DB_NAME`.`$TABLO`;" 2>/dev/null)
    printf "%-40s %sn" "$TABLO" "$SAYI" >> "$LOG_FILE"
    echo "  $TABLO: $SAYI kayıt"
done

echo "" >> "$LOG_FILE"
echo "Tamamlandı. Sonuçlar $LOG_FILE dosyasına yazıldı."

Bu scripti crontab’a ekleyerek günlük raporlar üretebilirsin:

# Her gece 02:00'de çalıştır
0 2 * * * /opt/scripts/tablo_kayit_sayisi.sh >> /var/log/cron_db_rapor.log 2>&1

Yöntem 5: information_schema ve ANALYZE TABLE Kombinasyonu

InnoDB tablolarında information_schema‘daki tahminleri güncellemek için önce ANALYZE TABLE çalıştırabilirsin. Bu işlem istatistikleri yeniler ve TABLE_ROWS değerini gerçeğe daha yakın hale getirir:

-- Önce istatistikleri güncelle
SELECT 
    CONCAT('ANALYZE TABLE `', TABLE_NAME, '`;') AS komut
FROM 
    information_schema.TABLES
WHERE 
    TABLE_SCHEMA = 'veritabani_adin'
    AND TABLE_TYPE = 'BASE TABLE';

Bu sorgunun çıktısını kopyalayıp çalıştırabilir, ardından Yöntem 1’deki sorguyu tekrar kullanabilirsin. Büyük veritabanlarında ANALYZE TABLE işlemi tablo kilitlemesine neden olabileceğinden, bu işlemi de düşük trafik saatlerinde yapmanı öneririm.

Gerçek Dünya Senaryosu 1: Veri Büyüme Takibi

Bir müşteri için yönettiğim e-ticaret veritabanında “orders” tablosu beklenenden çok daha hızlı büyüyordu. Sorunu tespit etmek için şu sorguyu kullandım:

SELECT 
    TABLE_NAME AS 'Tablo',
    TABLE_ROWS AS 'Tahmini Kayıt',
    ROUND(DATA_LENGTH / 1024 / 1024, 2) AS 'Veri (MB)',
    ROUND(INDEX_LENGTH / 1024 / 1024, 2) AS 'Index (MB)',
    ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS 'Toplam (MB)',
    CREATE_TIME AS 'Oluşturulma',
    UPDATE_TIME AS 'Son Güncelleme'
FROM 
    information_schema.TABLES
WHERE 
    TABLE_SCHEMA = 'eticaret_db'
    AND TABLE_TYPE = 'BASE TABLE'
ORDER BY 
    (DATA_LENGTH + INDEX_LENGTH) DESC
LIMIT 20;

Bu sorgu sayesinde hem kayıt sayısını hem de disk kullanımını tek sorguda gördüm. “order_logs” tablosunun beklenmedik şekilde şiştiğini fark ettim. Uygulama tarafında bir hata, her sipariş güncellemesinde gereksiz log kaydı yaratıyordu.

Gerçek Dünya Senaryosu 2: Migrate Sonrası Doğrulama

Bir veritabanı migrasyonu yaptıktan sonra kaynak ve hedef taraftaki kayıt sayılarının eşleşip eşleşmediğini kontrol etmek zorundasın. Bunun için şu yaklaşımı kullanıyorum:

-- Kaynak sunucuda çalıştır, sonuçları kaydet
SELECT 
    TABLE_NAME,
    TABLE_ROWS
FROM 
    information_schema.TABLES
WHERE 
    TABLE_SCHEMA = 'kaynak_db'
    AND TABLE_TYPE = 'BASE TABLE'
ORDER BY 
    TABLE_NAME
INTO OUTFILE '/tmp/kaynak_sayilar.csv'
FIELDS TERMINATED BY ','
LINES TERMINATED BY 'n';

Ardından hedef sunucuda aynı sorguyu çalıştırıp iki dosyayı karşılaştırabilirsin:

# Bash ile iki dosyayı karşılaştır
diff /tmp/kaynak_sayilar.csv /tmp/hedef_sayilar.csv

# Veya daha okunaklı bir karşılaştırma için
join -t',' -1 1 -2 1 
    <(sort /tmp/kaynak_sayilar.csv) 
    <(sort /tmp/hedef_sayilar.csv) | 
    awk -F',' '{
        fark = $2 - $3; 
        if (fark != 0) print $1 ": Kaynak=" $2 " Hedef=" $3 " Fark=" fark
    }'

Bu script sana sadece farklılık olan tabloları listeler. Migrasyon sonrası doğrulama sürecini çok hızlandırır.

Performans ve Dikkat Edilmesi Gerekenler

information_schema sorgularının kendisi çok hızlı olmakla birlikte, bazı durumlarda dikkat etmeni gereken noktalar var:

  • InnoDB istatistik hassasiyeti: InnoDB, TABLE_ROWS değerini her zaman canlı tutmaz. Çok sık write işlemi olan tablolarda bu değer gerçekten sapabilir.
  • COUNT() maliyeti: Özellikle partitioned olmayan büyük InnoDB tablolarında COUNT() full table scan yapabilir. Yüz milyonluk bir tabloda bu sorgu dakikalar alabilir.
  • Bağlantı havuzu: Bash scriptinde her tablo için ayrı bağlantı açmak yerine, mümkünse tek bağlantıda işlemleri bitir.
  • Yetki gereksinimleri: information_schema.TABLES sorgulayabilmek için kullanıcının o veritabanındaki tablolara en azından SELECT yetkisi olmalı. SHOW DATABASES ve SHOW TABLES yetkileri de işe yarar.
  • Replikasyon ortamları: Eğer replica sunucularda bu sorguları çalıştırıyorsan, replika lag nedeniyle master ile tutarsız sonuçlar görebilirsin.

Monitoring Entegrasyonu

Üretim ortamlarında bu bilgileri Zabbix, Prometheus veya Grafana ile entegre etmek çok değerli. Bir tablo aniden sıfıra düşüyorsa veya beklenmedik şekilde patlıyorsa alarm almak isteyebilirsin. Bunun için basit bir monitoring scripti:

#!/bin/bash
# Kritik tablolar icin kayit sayisi kontrolu
# /opt/scripts/kritik_tablo_kontrol.sh

DB_USER="monitor"
DB_PASS="monitor_parola"
DB_NAME="uygulama_db"
ALERT_EMAIL="[email protected]"

# Kontrol edilecek tablolar ve minimum kayıt sayıları
declare -A MIN_KAYIT
MIN_KAYIT["users"]=100
MIN_KAYIT["products"]=50
MIN_KAYIT["categories"]=10

HATA_VAR=0

for TABLO in "${!MIN_KAYIT[@]}"; do
    SAYI=$(mysql -u "$DB_USER" -p"$DB_PASS" -N -e 
        "SELECT COUNT(*) FROM `$DB_NAME`.`$TABLO`;" 2>/dev/null)
    
    MIN=${MIN_KAYIT[$TABLO]}
    
    if [ "$SAYI" -lt "$MIN" ]; then
        echo "UYARI: $TABLO tablosu sadece $SAYI kayıt iceriyor! (Min: $MIN)"
        HATA_VAR=1
    fi
done

if [ "$HATA_VAR" -eq 1 ]; then
    echo "Kritik tablo kayit sayisi sorunu tespit edildi!" | 
        mail -s "[ALARM] Veritabani Kayıt Sayisi Uyarisi" "$ALERT_EMAIL"
fi

Sık Yapılan Hatalar

Yıllar içinde gördüğüm en yaygın hataları sıralayayım:

  • information_schema’ya kör güvenmek: InnoDB tablolarda TABLE_ROWS her zaman yanlış olabilir. Kritik kararları bu veriye dayandırmadan önce COUNT(*) ile doğrula.
  • Production saatlerinde COUNT() çalıştırmak: Büyük tablolarda COUNT() ciddi I/O yükü yaratır. Mümkünse gece saatlerinde çalıştır ya da replica üzerinde kullan.
  • View’ları dahil etmek: TABLE_TYPE = 'BASE TABLE' filtresi olmazsa view’lar da listeye girer ve count sorguları hata verebilir. Bu filtreyi her zaman ekle.
  • Geçici tabloları saymaya çalışmak: information_schema‘da geçici tablolar görünmez, bu normal bir durum.
  • Karakter seti sorunları: Tablo adlarında Türkçe karakter veya boşluk varsa backtick kullanmayı unutma.

Farklı Veritabanı Motorlarının Etkisi

Sorgularını yazarken hangi storage engine kullandığını bilmek önemli. Kısaca özetleyeyim:

  • InnoDB: En yaygın kullanılan motor. TABLE_ROWS tahminidir, kesin değildir. COUNT(*) tam tarama yapabilir.
  • MyISAM: TABLE_ROWS kesin ve anlıktır çünkü MyISAM bu sayıyı tablonun header bilgisinde saklar. COUNT(*) çok hızlıdır.
  • MEMORY: MyISAM gibi kesin sayıyı tutar, hızlıdır ama sunucu yeniden başlayınca veriler gider.
  • ARCHIVE: Sadece INSERT ve SELECT destekler. COUNT(*) yavaş olabilir.

Veritabanındaki motorları kontrol etmek için:

SELECT 
    TABLE_NAME,
    ENGINE,
    TABLE_ROWS
FROM 
    information_schema.TABLES
WHERE 
    TABLE_SCHEMA = 'veritabani_adin'
    AND TABLE_TYPE = 'BASE TABLE'
ORDER BY 
    ENGINE, TABLE_NAME;

Bu sorgu sana hangi tablonun hangi motorla çalıştığını gösterir ve TABLE_ROWS değerine ne kadar güvenebileceğini anlamana yardımcı olur.

Sonuç

Tüm tablolardaki kayıt sayısını sorgulamak kulağa basit bir iş gibi gelir ama doğru yöntemi seçmek önemlidir. Hızlı bir tahmini görmek için information_schema.TABLES sorgusunu kullan. Kesin sayıya ihtiyacın varsa ve veritabanı küçükse stored procedure veya bash script yöntemini tercih et. Production ortamlarında büyük tablolar varsa bu işleri replica sunuculara yönlendir ve düşük trafik saatlerinde çalıştır.

Bu sorguları monitoring ve otomasyon süreçlerine entegre ettiğinde veritabanının durumunu çok daha proaktif takip edebilirsin. Beklenmedik veri kayıplarını veya anormal büyümeleri erkenden fark etmek, bir sysadmin olarak seni pek çok gece krizinden kurtarır. Metodları kendi ortamına göre uyarla, gerektiğinde index istatistiklerini tazele ve her zaman önce replica ortamında test et.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir