MariaDB Columnstore ile Analitik Sorgular

Büyük veri analizi söz konusu olduğunda, klasik row-based (satır tabanlı) veritabanı motorları genellikle yetersiz kalır. Milyonlarca satırı tararken dakikalarca beklemek, raporlama sorgularının yavaşlaması ve sunucu kaynaklarının gereksiz tükenmesi… Bunları yaşıyorsanız MariaDB Columnstore tam size göre. Bu yazıda Columnstore motorunu gerçek dünya senaryolarıyla inceleyeceğiz, kurulumdan performans optimizasyonuna kadar her şeyi ele alacağız.

MariaDB Columnstore Nedir?

MariaDB Columnstore, verileri satır bazında değil sütun bazında depolayan ve analitik sorgular için optimize edilmiş bir depolama motorudur. InfiniDB projesinden türetilmiş olan bu motor, OLAP (Online Analytical Processing) iş yükleri için tasarlanmıştır.

Klasik InnoDB motorundan temel farkı şu: InnoDB bir sorgu çalıştırdığında ilgili satırların tamamını okur. Columnstore ise sadece sorguda kullanılan sütunları okur. Bu, özellikle geniş tablolarda (50+ sütun) muazzam bir I/O tasarrufu sağlar.

Ne zaman Columnstore kullanmalısınız:

  • Milyonlarca veya milyarlarca satırlık tablolarınız varsa
  • Sorgularınız büyük veri setleri üzerinde aggregation (SUM, AVG, COUNT, GROUP BY) yapıyorsa
  • Raporlama ve BI (Business Intelligence) iş yükleri çalıştırıyorsanız
  • Veri ambarı mimarisi kuruyorsanız
  • Batch insert ağırlıklı yazma operasyonlarınız varsa

Ne zaman Columnstore kullanmamalısınız:

  • Tek satır okuma/yazma operasyonları çoksa (OLTP iş yükleri)
  • Sık UPDATE/DELETE işlemi yapıyorsanız
  • Küçük boyutlu tablolarla çalışıyorsanız

Kurulum ve Yapılandırma

MariaDB Columnstore Kurulumu

Önce MariaDB sunucunuzun kurulu olduğundan emin olun. Columnstore eklentisi MariaDB 10.5 ve üzeri sürümlerde daha stabil çalışır.

# Ubuntu/Debian için MariaDB repo ekleyin
curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash

# MariaDB Columnstore paketini kurun
sudo apt-get install mariadb-plugin-columnstore

# Ya da tam Columnstore kurulumu için
sudo apt-get install mariadb-columnstore

Red Hat/CentOS tabanlı sistemler için:

# MariaDB reposunu ekleyin
sudo rpm --import https://downloads.mariadb.com/MariaDB/RPM-GPG-KEY-MariaDB

# Columnstore motorunu kurun
sudo yum install MariaDB-columnstore-engine

# Servisi başlatın
sudo systemctl start mariadb-columnstore
sudo systemctl enable mariadb-columnstore

Kurulumdan sonra eklentinin yüklü olup olmadığını doğrulayın:

# MariaDB'ye bağlanın
mysql -u root -p

# Eklentileri listeleyin
SHOW PLUGINS;

# Columnstore motorunu kontrol edin
SELECT * FROM information_schema.ENGINES WHERE ENGINE = 'Columnstore';

Temel Yapılandırma

Columnstore’un varsayılan yapılandırması çoğu senaryo için yeterlidir, ancak üretim ortamı için bazı ayarları optimize etmek gerekir. /etc/columnstore/Columnstore.xml dosyasını düzenleyin:

# Columnstore yapılandırma dosyasını düzenleyin
sudo nano /etc/columnstore/Columnstore.xml

# Önemli parametreler:
# <NumBlocksPct>70</NumBlocksPct>  --> Bellek havuzunun yüzdesi
# <TotalUmMemory>4G</TotalUmMemory> --> User Module bellek limiti
# <MaxColumnstoreJobsPerQuery>16</MaxColumnstoreJobsPerQuery> --> Paralel iş sayısı

İlk Columnstore Tablonuzu Oluşturun

Gerçekçi bir senaryo üzerinden gidelim. Bir e-ticaret firması için satış analitik tablosu oluşturacağız.

-- Analitik veritabanı oluşturun
CREATE DATABASE IF NOT EXISTS sales_analytics;
USE sales_analytics;

-- Columnstore motoru ile satış olayları tablosu
CREATE TABLE sales_events (
    event_id        BIGINT NOT NULL,
    event_date      DATE NOT NULL,
    event_timestamp DATETIME NOT NULL,
    customer_id     INT NOT NULL,
    product_id      INT NOT NULL,
    category_id     INT NOT NULL,
    region_id       SMALLINT NOT NULL,
    quantity        INT NOT NULL,
    unit_price      DECIMAL(10,2) NOT NULL,
    total_amount    DECIMAL(12,2) NOT NULL,
    discount_rate   DECIMAL(5,2) DEFAULT 0.00,
    channel         VARCHAR(20) NOT NULL,
    campaign_id     INT DEFAULT NULL
) ENGINE=Columnstore DEFAULT CHARSET=utf8mb4;

-- Boyut tabloları InnoDB olabilir (lookup için)
CREATE TABLE products (
    product_id   INT PRIMARY KEY AUTO_INCREMENT,
    product_name VARCHAR(200) NOT NULL,
    category_id  INT NOT NULL,
    brand        VARCHAR(100),
    cost_price   DECIMAL(10,2)
) ENGINE=InnoDB;

Dikkat: Columnstore tablolarında PRIMARY KEY, UNIQUE INDEX ve FOREIGN KEY desteklenmez. Bu kısıtlamayı aklınızda tutun.

Veri Yükleme Stratejileri

Columnstore’a veri yüklerken performansı maksimize etmek için doğru yöntemi seçmek kritik önem taşır.

Bulk Load ile Hızlı Veri Aktarımı

# CSV dosyasından hızlı veri yükleme
# cpimport aracı Columnstore'a özel ve çok daha hızlı
sudo cpimport sales_analytics sales_events /tmp/sales_data.csv 
    -s ',' 
    -E '"' 
    -n 1

# Alternatif olarak LOAD DATA INFILE kullanabilirsiniz
# Ama cpimport genellikle 3-5x daha hızlıdır

Eğer mevcut bir InnoDB tablosundan Columnstore’a geçiş yapıyorsanız:

-- InnoDB'den Columnstore'a veri kopyalama
INSERT INTO sales_analytics.sales_events
SELECT 
    event_id,
    event_date,
    event_timestamp,
    customer_id,
    product_id,
    category_id,
    region_id,
    quantity,
    unit_price,
    total_amount,
    discount_rate,
    channel,
    campaign_id
FROM legacy_db.sales_raw
WHERE event_date >= '2020-01-01';

-- Büyük tablolar için parçalı yükleme yapın
-- Her seferinde 1 milyonluk batch
INSERT INTO sales_analytics.sales_events
SELECT * FROM legacy_db.sales_raw
WHERE event_date BETWEEN '2023-01-01' AND '2023-03-31';

Analitik Sorgu Örnekleri

İşte Columnstore’un gerçek gücünün ortaya çıktığı yer burası. Aynı sorguları InnoDB’de çalıştırdığınızda dakikalar sürebilir, Columnstore’da saniyeler içinde sonuç alırsınız.

Senaryo 1: Aylık Satış Performans Raporu

-- Aylık bazda satış özeti - 500 milyon satır üzerinde bile hızlı çalışır
SELECT 
    YEAR(event_date)                    AS satis_yili,
    MONTH(event_date)                   AS satis_ayi,
    COUNT(*)                            AS islem_sayisi,
    COUNT(DISTINCT customer_id)         AS benzersiz_musteri,
    SUM(quantity)                       AS toplam_adet,
    SUM(total_amount)                   AS toplam_ciro,
    AVG(total_amount)                   AS ortalama_siparis_degeri,
    SUM(total_amount * discount_rate / 100) AS toplam_indirim,
    MAX(total_amount)                   AS en_buyuk_siparis
FROM sales_events
WHERE event_date BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY 
    YEAR(event_date), 
    MONTH(event_date)
ORDER BY 
    satis_yili, 
    satis_ayi;

Senaryo 2: Ürün Kategori Analizi

-- Kategori bazında performans metrikleri
SELECT 
    se.category_id,
    se.channel,
    COUNT(DISTINCT se.customer_id)              AS musteri_sayisi,
    COUNT(*)                                    AS siparis_adedi,
    SUM(se.total_amount)                        AS kategori_ciro,
    SUM(se.total_amount) / COUNT(*)             AS ortalama_siparis,
    SUM(se.quantity)                            AS satis_adedi,
    AVG(se.discount_rate)                       AS ort_indirim_orani,
    SUM(se.total_amount) * 100.0 / 
        SUM(SUM(se.total_amount)) OVER()        AS ciro_yuzdesi
FROM sales_events se
WHERE 
    se.event_date >= DATE_SUB(CURDATE(), INTERVAL 90 DAY)
    AND se.channel IN ('web', 'mobile', 'store')
GROUP BY 
    se.category_id,
    se.channel
ORDER BY 
    kategori_ciro DESC
LIMIT 50;

Senaryo 3: Cohort Analizi (Müşteri Segment Takibi)

Bu tür analitik sorgular klasik RDBMS’lerde gerçekten ağır çalışır:

-- Müşterinin ilk alışveriş ayı bazında cohort analizi
WITH first_purchase AS (
    SELECT 
        customer_id,
        MIN(event_date)                    AS ilk_alis_tarihi,
        DATE_FORMAT(MIN(event_date), '%Y-%m') AS cohort_ayi
    FROM sales_events
    GROUP BY customer_id
),
monthly_activity AS (
    SELECT 
        se.customer_id,
        fp.cohort_ayi,
        DATE_FORMAT(se.event_date, '%Y-%m')  AS aktif_ay,
        PERIOD_DIFF(
            DATE_FORMAT(se.event_date, '%Y%m'),
            DATE_FORMAT(fp.ilk_alis_tarihi, '%Y%m')
        )                                    AS ay_farki,
        SUM(se.total_amount)                 AS aylik_harcama
    FROM sales_events se
    JOIN first_purchase fp ON se.customer_id = fp.customer_id
    GROUP BY 
        se.customer_id, 
        fp.cohort_ayi, 
        aktif_ay,
        ay_farki
)
SELECT 
    cohort_ayi,
    ay_farki,
    COUNT(DISTINCT customer_id) AS aktif_musteri,
    SUM(aylik_harcama)          AS cohort_ciro,
    AVG(aylik_harcama)          AS kisi_basi_harcama
FROM monthly_activity
GROUP BY 
    cohort_ayi, 
    ay_farki
ORDER BY 
    cohort_ayi, 
    ay_farki;

Performans Optimizasyonu

Sorgu Planını Anlamak

-- Columnstore'da sorgu planını inceleyin
EXPLAIN SELECT 
    region_id,
    SUM(total_amount) AS toplam
FROM sales_events
WHERE event_date >= '2023-01-01'
GROUP BY region_id;

-- Daha detaylı plan için
EXPLAIN EXTENDED SELECT ...;

-- Columnstore'a özgü istatistikleri kontrol edin
SELECT * FROM information_schema.COLUMNSTORE_TABLES
WHERE table_schema = 'sales_analytics';

Extent Elimination ile Performans

Columnstore, verileri extent adı verilen 8MB’lık bloklara böler ve her extent için min/max değerleri tutar. Sorgularınızda filtreleme kullandığınızda motor gereksiz extent’leri okumaz. Buna extent elimination denir.

Bu özellikten maksimum fayda sağlamak için:

-- Kötü: Tarihi string olarak filtrelemek
SELECT * FROM sales_events 
WHERE DATE_FORMAT(event_date, '%Y') = '2023';  -- Extent elimination çalışmaz!

-- İyi: Native date filtresi kullanmak
SELECT * FROM sales_events 
WHERE event_date BETWEEN '2023-01-01' AND '2023-12-31';  -- Extent elimination çalışır!

-- Kötü: Sütun üzerinde fonksiyon kullanmak
SELECT * FROM sales_events 
WHERE YEAR(event_date) = 2023;  -- Tüm extent'ler okunur

-- İyi: Range filtreleme
SELECT * FROM sales_events 
WHERE event_date >= '2023-01-01' 
  AND event_date < '2024-01-01';  -- Sadece ilgili extent'ler okunur

Paralel Sorgu Ayarları

-- Mevcut session için paralellik seviyesini ayarlayın
SET columnstore_use_import_for_batchinsert = ON;

-- İnfiniDB uyumlu paralel sorgu modu
SET infinidb_local_query = 0;  -- Distributed mode
SET infinidb_local_query = 1;  -- Local mode (tek node)

-- Bellek kullanımını sorgulayın
SELECT 
    variable_name,
    variable_value
FROM information_schema.GLOBAL_STATUS
WHERE variable_name LIKE 'Columnstore%';

Hibrit Mimari: Columnstore + InnoDB Birlikte Kullanım

Gerçek dünya uygulamalarında çoğunlukla her iki motoru birlikte kullanırsınız. OLTP işlemler için InnoDB, raporlama için Columnstore.

-- Örnek: InnoDB OLTP tablosu ile Columnstore analitik tablosunu birleştirme
-- Müşteri detayları InnoDB'de, satış verileri Columnstore'da

CREATE TABLE customers (
    customer_id   INT PRIMARY KEY AUTO_INCREMENT,
    customer_name VARCHAR(200) NOT NULL,
    email         VARCHAR(150) UNIQUE,
    city          VARCHAR(100),
    segment       ENUM('bronze','silver','gold','platinum'),
    created_at    DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

-- Analitik sorgu: Her iki tabloyu birleştir
SELECT 
    c.segment,
    c.city,
    COUNT(DISTINCT se.customer_id)  AS musteri_sayisi,
    SUM(se.total_amount)            AS toplam_harcama,
    AVG(se.total_amount)            AS kisi_basi_harcama,
    SUM(se.quantity)                AS toplam_adet
FROM sales_events se
JOIN customers c ON se.customer_id = c.customer_id
WHERE 
    se.event_date >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR)
    AND c.segment IN ('gold', 'platinum')
GROUP BY 
    c.segment,
    c.city
ORDER BY 
    toplam_harcama DESC;

İzleme ve Bakım

Columnstore tablolarının sağlığını düzenli kontrol etmek gerekir:

# Columnstore tablolarının durumunu kontrol edin
sudo mcsadmin getSystemStatus

# Tablo istatistiklerini yenileyin (sorgu planı iyileştirmesi için)
mysqlcheck --analyze --user=root --password sales_analytics

# Columnstore log dosyalarını inceleyin
sudo tail -f /var/log/mariadb/columnstore/err.log
sudo tail -f /var/log/mariadb/columnstore/debug.log
-- Tablo boyutu ve sıkıştırma bilgisi
SELECT 
    table_name,
    table_rows,
    ROUND(data_length / 1024 / 1024, 2)     AS data_mb,
    ROUND(index_length / 1024 / 1024, 2)    AS index_mb,
    ROUND((data_length + index_length) / 1024 / 1024, 2) AS toplam_mb
FROM information_schema.TABLES
WHERE 
    table_schema = 'sales_analytics'
    AND engine = 'Columnstore'
ORDER BY data_length DESC;

-- Columnstore motorunun genel durumu
SELECT 
    variable_name, 
    variable_value
FROM information_schema.GLOBAL_STATUS
WHERE variable_name LIKE '%columnstore%'
ORDER BY variable_name;

Yaygın Hatalar ve Çözümleri

Sahada en sık karşılaştığım sorunlar ve çözümleri:

Hata: “No ColumnStore engine support for type” hatası

  • Columnstore bazı veri tiplerini desteklemez: BLOB, TEXT, SET, BIT, GEOMETRY
  • Bu alanlar için VARCHAR veya INT dönüşümü yapın

Hata: Sorgular çok yavaş çalışıyor

  • SET infinidb_local_query = 0 komutunu çalıştırıp dağıtık modu aktif edin
  • WHERE koşullarının extent elimination’dan faydalanıp faydalanmadığını kontrol edin
  • EXPLAIN çıktısında “CS” prefiksi görmelisiniz, görmüyorsanız Columnstore kullanılmıyor demektir

Hata: INSERT performansı düşük

  • Tek tek INSERT yerine batch INSERT kullanın
  • Mümkünse cpimport aracını tercih edin
  • columnstore_use_import_for_batchinsert = ON ayarını etkinleştirin

Hata: Bellek yetersizliği

  • /etc/columnstore/Columnstore.xml dosyasında TotalUmMemory değerini artırın
  • Sunucunuzun RAM’inin en az %60’ını Columnstore’a ayırın

Gerçek Dünya Senaryosu: E-Ticaret Raporlama Sistemi

Bir müşterimizin projesinde yaklaşık 800 milyon satırlık satış verisi mevcuttu. InnoDB ile çalışan eski raporlama sistemi, günlük satış raporunu üretmek için 45-60 dakika harcıyordu. Columnstore’a geçişten sonra:

  • Günlük satış raporu: 45 dakikadan 38 saniyeye indi
  • Aylık cohort analizi: 2 saatten 4 dakikaya düştü
  • Depolama alanı: Sıkıştırma sayesinde yaklaşık %65 azaldı
  • Sunucu CPU kullanımı: Raporlama sırasında %95’ten %40’a geriledi

Bu geçiş için kullandığımız temel strateji şuydu: OLTP işlemleri için InnoDB tablolarını koruyup her gece cpimport ile Columnstore tablolarına toplu veri aktarımı yaptık. Bu hibrit yaklaşım hem yazma performansını hem de okuma performansını optimize etti.

Sonuç

MariaDB Columnstore, büyük veri analitikleri için ciddi bir seçenek. Özellikle şunları aklınızda tutun:

  • Doğru iş yükü seçimi kritik: OLAP için Columnstore, OLTP için InnoDB
  • Extent elimination mekanizmasından faydalanmak için WHERE koşullarınızı akıllıca yazın
  • Bulk load için mutlaka cpimport aracını kullanın
  • Hibrit mimari çoğu zaman en iyi sonucu verir
  • Desteklenmeyen veri tipleri için şema dönüşümü yapın

MariaDB Columnstore’u InnoDB ile karşılaştırırken “hangisi daha iyi?” sorusunu sormak yerine “hangi iş yükü için hangi motor?” sorusunu sormalısınız. İkisini birlikte, doğru senaryolarda kullandığınızda muazzam bir performans kazanımı elde edersiniz. Sorularınız varsa yorum bölümüne yazabilirsiniz, elimden geldiğince yardımcı olmaya çalışırım.

Yorum yapın