📌 Ne Zaman Kullanılır?
- ✅ Mobil uygulama, embedded sistem, test/prototip, tek kullanıcılı uygulama, Electron
- ⚠️ Concurrent write sınırlı (WAL mode ile okuma iyileşir)
- ❌ Çok kullanıcılı web uygulaması, yüksek yazma trafiği
Önerilen Kullanım: Mobil + test veritabanı + embedded + küçük proje Alternatifler: MySQL (web app), PostgreSQL (gelişmiş), LiteFS (dağıtık SQLite)
SQLite
Ne Zaman Kullanılır? (When to Use?)
- Embedded uygulamalar: Mobil (iOS/Android), masaüstü uygulamalar
- Geliştirme/test ortamı: Hızlı prototipleme, unit testler
- Küçük-orta ölçekli web uygulamaları: Düşük trafik siteleri
- Veri analizi: Tek kullanıcılı veri işleme, CSV import/export
Python ile Kullanım (Python Usage)
import sqlite3
# Veritabanina baglan (dosya yoksa olusturur)
# Connect to database (creates file if not exists)
conn = sqlite3.connect('myapp.db')
conn.row_factory = sqlite3.Row # Dict-like rows
cursor = conn.cursor()
# Tablo olustur / Create table
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
age INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
)
''')
# INSERT
cursor.execute(
'INSERT INTO users (name, email, age) VALUES (?, ?, ?)',
('Ali Yilmaz', 'ali@test.com', 28)
)
# Toplu INSERT / Bulk insert
users_data = [
('Ayse Demir', 'ayse@test.com', 32),
('Mehmet Kaya', 'mehmet@test.com', 25),
]
cursor.executemany('INSERT INTO users (name, email, age) VALUES (?, ?, ?)', users_data)
# SELECT
cursor.execute('SELECT * FROM users WHERE age > ?', (25,))
rows = cursor.fetchall()
for row in rows:
print(f"{row['name']} - {row['email']}")
# UPDATE
cursor.execute('UPDATE users SET age = ? WHERE email = ?', (30, 'ali@test.com'))
# DELETE
cursor.execute('DELETE FROM users WHERE id = ?', (3,))
# Kaydet ve kapat / Commit and close
conn.commit()
conn.close()
# Context manager kullanimi / Using context manager
with sqlite3.connect('myapp.db') as conn:
conn.execute('SELECT * FROM users')CLI Komutları (CLI Commands)
# SQLite shell ac / Open SQLite shell
sqlite3 myapp.db
# Faydali komutlar / Useful commands
.tables # Tablolari listele
.schema users # Tablo semasini goster
.mode column # Sutun gorunumu
.headers on # Baslik satirlarini goster
.mode csv # CSV cikti modu
.output result.csv # Ciktiyi dosyaya yonlendir
.import data.csv users # CSV'den icerik aktar
.dump # Tum veritabanini SQL olarak cikart
.quit # CikisCRUD
-- SQLite ozellikleri / SQLite specifics
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- AUTOINCREMENT (not AUTO_INCREMENT)
name TEXT NOT NULL,
price REAL NOT NULL,
in_stock INTEGER DEFAULT 1, -- Boolean as INTEGER (0/1)
data TEXT, -- JSON stored as TEXT
created_at TEXT DEFAULT (datetime('now','localtime'))
);
-- UPSERT
INSERT INTO products (name, price) VALUES ('Widget', 9.99)
ON CONFLICT(name) DO UPDATE SET price = excluded.price;
-- Tarih islemleri / Date operations
SELECT * FROM products WHERE created_at > datetime('now', '-7 days');
SELECT strftime('%Y-%m', created_at) AS month, COUNT(*) FROM orders GROUP BY month;
-- LIMIT ve OFFSET
SELECT * FROM products ORDER BY price DESC LIMIT 10 OFFSET 20;Veri Tipleri ve Type Affinity (Data Types & Type Affinity)
SQLite dinamik tip sistemine sahiptir. Deklare edilen tip bir "öneri" niteliğindedir; veri herhangi bir sütuna yazılabilir.
5 Storage Class
| Storage Class | Açıklama |
|---|---|
| NULL | Boş değer |
| INTEGER | 1, 2, 4, 6 veya 8 byte tam sayı |
| REAL | 8 byte IEEE floating point |
| TEXT | UTF-8/UTF-16 metin |
| BLOB | Ham binary veri |
Type Affinity Kuralları (Type Affinity Rules)
SQLite, deklare edilen tipe göre bir affinity atar. Bu affinity, gelen verinin hangi storage class'a dönüştürüleceğini belirler:
| Deklare Edilen Tip İçeriği | Affinity | Örnek |
|---|---|---|
| INT | INTEGER | INT, BIGINT, SMALLINT |
| CHAR, CLOB, TEXT | TEXT | VARCHAR(255), TEXT |
| BLOB veya tip belirtilmemiş | BLOB | BLOB, tip yok |
| REAL, FLOA, DOUB | REAL | REAL, DOUBLE, FLOAT |
| Diğer her şey | NUMERIC | BOOLEAN, DATE, DECIMAL |
-- Affinity ornegi: VARCHAR deklare edilse bile TEXT affinity uygulanir
CREATE TABLE demo (
id INTEGER PRIMARY KEY,
name VARCHAR(100), -- TEXT affinity
score REAL, -- REAL affinity
data BLOB -- BLOB affinity
);
-- Tip kontrolu
SELECT typeof(name), typeof(score), typeof(data) FROM demo;Strict Tables (SQLite 3.37+)
Strict modda SQLite, tip uyumsuzluklarında hata verir. Dinamik tiplemenin istenmediği durumlarda kullanılır:
CREATE TABLE strict_users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INTEGER NOT NULL,
balance REAL DEFAULT 0.0
) STRICT;
-- Hata verir: TEXT sutununa INTEGER yazma denemesi
-- INSERT INTO strict_users (id, name, age) VALUES (1, 123, 25);
-- Error: cannot store INTEGER value in TEXT column strict_users.nameWAL Mode (Write-Ahead Logging)
-- WAL modunu etkinlestir / Enable WAL mode (better concurrent reads)
PRAGMA journal_mode=WAL;
-- Diger faydali pragma'lar / Other useful pragmas
PRAGMA foreign_keys = ON; -- FK dogrulamasini etkinlestir
PRAGMA cache_size = -20000; -- 20MB cache
PRAGMA synchronous = NORMAL; -- Performans/guvenlik dengesi
PRAGMA temp_store = MEMORY; -- Gecici tablolar bellekteAdvanced Queries
CTE (Common Table Expressions)
-- Basit CTE: Departman bazinda ortalama maas
WITH dept_avg AS (
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
)
SELECT e.name, e.salary, d.avg_salary
FROM employees e
JOIN dept_avg d ON e.department = d.department
WHERE e.salary > d.avg_salary;Recursive CTE -- Ağaç Yapısı (Tree Structure)
-- Kategori agaci (parent-child iliskisi)
CREATE TABLE categories (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
parent_id INTEGER REFERENCES categories(id)
);
-- Tum alt kategorileri recursive olarak getir
WITH RECURSIVE category_tree AS (
-- Baslangic: kok dugum
SELECT id, name, parent_id, 0 AS depth
FROM categories
WHERE parent_id IS NULL
UNION ALL
-- Recursive adim: cocuklari ekle
SELECT c.id, c.name, c.parent_id, ct.depth + 1
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT id, printf('%.*c', depth * 2, ' ') || name AS tree_name, depth
FROM category_tree
ORDER BY id;Window Functions
-- ROW_NUMBER: Her departmanda maasa gore siralama
SELECT
name,
department,
salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_in_dept
FROM employees;
-- RANK ve DENSE_RANK farki
SELECT
name,
salary,
RANK() OVER (ORDER BY salary DESC) AS rank,
DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees;
-- LAG ve LEAD: Onceki/sonraki satir degeri
SELECT
date,
revenue,
LAG(revenue, 1) OVER (ORDER BY date) AS prev_day_revenue,
LEAD(revenue, 1) OVER (ORDER BY date) AS next_day_revenue,
revenue - LAG(revenue, 1) OVER (ORDER BY date) AS daily_change
FROM daily_sales;JSON Fonksiyonlari
-- JSON veri saklama ve sorgulama
CREATE TABLE events (
id INTEGER PRIMARY KEY,
data TEXT -- JSON olarak saklanir
);
INSERT INTO events (data) VALUES ('{"type":"click","x":100,"y":200,"tags":["ui","btn"]}');
-- json_extract: Belirli bir alan cek
SELECT json_extract(data, '$.type') AS event_type FROM events;
-- json_each: JSON array'i satir satir ac
SELECT e.id, j.value AS tag
FROM events e, json_each(json_extract(e.data, '$.tags')) j;
-- json_group_array: Sonuclari JSON array olarak topla
SELECT department, json_group_array(name) AS members
FROM employees
GROUP BY department;
-- json_set: JSON icerigini guncelle
UPDATE events
SET data = json_set(data, '$.x', 150, '$.processed', true)
WHERE id = 1;Triggers ve Views (Triggers & Views)
Triggers
-- AFTER INSERT: Yeni kayit eklendiginde log tut
CREATE TRIGGER log_user_insert
AFTER INSERT ON users
BEGIN
INSERT INTO audit_log (table_name, action, record_id, created_at)
VALUES ('users', 'INSERT', NEW.id, datetime('now'));
END;
-- BEFORE UPDATE: updated_at otomatik guncelle
CREATE TRIGGER update_timestamp
BEFORE UPDATE ON users
BEGIN
UPDATE users SET updated_at = datetime('now') WHERE id = NEW.id;
END;
-- AFTER DELETE: Silinen kaydi arsivle
CREATE TRIGGER archive_deleted_user
AFTER DELETE ON users
BEGIN
INSERT INTO deleted_users (original_id, name, email, deleted_at)
VALUES (OLD.id, OLD.name, OLD.email, datetime('now'));
END;Trigger kullanım senaryoları:
- Audit log: Tüm değişikliklerin otomatik kaydını tutma
- updated_at: Zaman damgasını her güncelleme öncesi otomatik ayarlama
- Cascade işlemleri: İlişkili tablolardaki kayıtları otomatik güncelleme/silme
- Veri doğrulama: INSERT/UPDATE öncesi iş kuralı kontrolü
Views
-- Karmasik sorguyu view olarak tanimla
CREATE VIEW active_user_summary AS
SELECT
u.id,
u.name,
u.email,
COUNT(o.id) AS total_orders,
COALESCE(SUM(o.total), 0) AS total_spent,
MAX(o.created_at) AS last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.is_active = 1
GROUP BY u.id;
-- View kullanimi (normal tablo gibi sorgulanir)
SELECT * FROM active_user_summary WHERE total_spent > 1000;
-- View silme
DROP VIEW IF EXISTS active_user_summary;Transactions
Temel Kullanım (Basic Usage)
-- Basarili transaction
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- Hata durumunda geri alma
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- Bir hata olusursa:
ROLLBACK;Transaction Türleri (Transaction Types)
-- DEFERRED (varsayilan): Ilk veri erisiminde kilit alinir
BEGIN DEFERRED TRANSACTION;
-- IMMEDIATE: Basladiginda yazma kilidi alinir, okumaya izin verir
BEGIN IMMEDIATE TRANSACTION;
-- EXCLUSIVE: Basladiginda tum erisimi kilitler
BEGIN EXCLUSIVE TRANSACTION;| Tür | Okuma | Yazma | Ne Zaman Kullanılır |
|---|---|---|---|
| DEFERRED | Hemen | Gerektiğinde | Varsayılan, çoğu durum |
| IMMEDIATE | Evet | Hemen kilitli | Yazma garanti edilecekse |
| EXCLUSIVE | Kilitli | Kilitli | Tam izolasyon gerektiğinde |
Nested Transactions (SAVEPOINT)
BEGIN TRANSACTION;
INSERT INTO orders (user_id, total) VALUES (1, 250);
SAVEPOINT order_items;
INSERT INTO order_items (order_id, product_id, qty) VALUES (1, 10, 2);
INSERT INTO order_items (order_id, product_id, qty) VALUES (1, 20, 1);
-- Bu adimda sorun olursa sadece order_items geri alinir
RELEASE SAVEPOINT order_items;
-- veya hata durumunda:
-- ROLLBACK TO SAVEPOINT order_items;
COMMIT;Performance
PRAGMA Optimizasyonları (PRAGMA Optimizations)
| PRAGMA | Değer | Açıklama |
|---|---|---|
journal_mode | WAL | Concurrent okuma iyileştirmesi |
cache_size | -20000 | 20 MB bellek cache (negatif = KB) |
synchronous | NORMAL | Performans/güvenlik dengesi (WAL ile güvenli) |
mmap_size | 268435456 | 256 MB memory-mapped I/O |
temp_store | MEMORY | Geçici tabloları bellekte tut |
page_size | 4096 | Sayfa boyutu (veritabanı oluşturulmadan önce ayarla) |
auto_vacuum | INCREMENTAL | Otomatik alan geri kazanımı |
-- Uygulama baslatildiginda calistirilacak pragma'lar
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA cache_size = -20000;
PRAGMA mmap_size = 268435456;
PRAGMA temp_store = MEMORY;
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;İndeks Stratejisi (Index Strategy)
-- Tek sutun index
CREATE INDEX idx_users_email ON users(email);
-- Composite index (soldan saga kullanilir)
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
-- Partial index (sadece belirli kosulu saglayan satirlar)
CREATE INDEX idx_active_users ON users(email) WHERE is_active = 1;
-- Unique index
CREATE UNIQUE INDEX idx_users_username ON users(username);
-- Index kullanilip kullanilmadigini kontrol et
EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = 'test@test.com';EXPLAIN QUERY PLAN
-- Sorgu planini incele
EXPLAIN QUERY PLAN
SELECT u.name, COUNT(o.id)
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.is_active = 1
GROUP BY u.id;
-- Cikti ornegi:
-- SEARCH TABLE users USING INDEX idx_active_users (is_active=?)
-- SEARCH TABLE orders USING INDEX idx_orders_user_id (user_id=?)SCAN TABLE goruyorsaniz index eksik demektir. SEARCH TABLE ... USING INDEX ideal sonuctur.
Backup
-- CLI ile backup
sqlite3 myapp.db ".backup backup.db"
-- SQL dump (metin formati, versiyon bagimsiz)
sqlite3 myapp.db ".dump" > backup.sql
-- Geri yukleme
sqlite3 restored.db < backup.sql# Python ile online backup (veritabani acikken)
import sqlite3
source = sqlite3.connect('myapp.db')
backup = sqlite3.connect('backup.db')
source.backup(backup)
backup.close()
source.close()VACUUM
-- Veritabani dosyasini sikistir (fragmentation gider, boyut kucultme)
VACUUM;
-- Otomatik vacuum ayarla (yeni veritabani icin)
PRAGMA auto_vacuum = INCREMENTAL;
-- Incremental vacuum: Belirli sayfa kadar bosalt
PRAGMA incremental_vacuum(100);VACUUM işleminin tüm veritabanını yeniden yazdığı için büyük veritabanlarında zaman alabilir. auto_vacuum = INCREMENTAL ile parçalı temizlik tercih edilebilir.
Kullanım Senaryoları (Use Cases)
Electron Uygulamaları
// better-sqlite3 -- senkron, hizli, native
const Database = require('better-sqlite3');
const db = new Database('app.db');
// Senkron API (Electron main process icin ideal)
const user = db.prepare('SELECT * FROM users WHERE id = ?').get(1);
const users = db.prepare('SELECT * FROM users').all();
// Transaction
const insertMany = db.transaction((items) => {
const stmt = db.prepare('INSERT INTO items (name, value) VALUES (?, ?)');
for (const item of items) stmt.run(item.name, item.value);
});
insertMany([{ name: 'a', value: 1 }, { name: 'b', value: 2 }]);// sql.js -- pure JavaScript, WASM tabanli (native build gerektirmez)
const initSqlJs = require('sql.js');
const SQL = await initSqlJs();
const db = new SQL.Database();
db.run('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)');
db.run('INSERT INTO test VALUES (?, ?)', [1, 'hello']);
const result = db.exec('SELECT * FROM test');
// result: [{ columns: ['id', 'name'], values: [[1, 'hello']] }]| Özellik | better-sqlite3 | sql.js |
|---|---|---|
| Performans | Çok hızlı (native) | Yavaş (WASM) |
| Build | Native addon gerekir | Pure JS, build gereksiz |
| API | Senkron | Senkron (in-memory) |
| Dosya I/O | Doğrudan | Manuel load/save |
| Kullanım | Electron main process | Browser, worker, cross-platform |
React Native / Flutter
// React Native: expo-sqlite
import * as SQLite from 'expo-sqlite';
const db = await SQLite.openDatabaseAsync('app.db');
await db.execAsync(`
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
done INTEGER DEFAULT 0
);
`);
const result = await db.getAllAsync('SELECT * FROM tasks WHERE done = ?', [0]);// Flutter: sqflite
import 'package:sqflite/sqflite.dart';
final db = await openDatabase('app.db', version: 1,
onCreate: (Database db, int version) async {
await db.execute('''
CREATE TABLE tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
done INTEGER DEFAULT 0
)
''');
});
List<Map> tasks = await db.query('tasks', where: 'done = ?', whereArgs: [0]);Test Veritabanı Olarak (As Test Database)
# pytest: In-memory veritabani ile test
import sqlite3
import pytest
@pytest.fixture
def db():
conn = sqlite3.connect(':memory:')
conn.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)')
yield conn
conn.close()
def test_insert_user(db):
db.execute('INSERT INTO users (name) VALUES (?)', ('Test',))
result = db.execute('SELECT name FROM users').fetchone()
assert result[0] == 'Test'# Django: Test icin SQLite kullan (hiz icin)
# settings/test.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
}// Laravel: phpunit.xml icinde SQLite test config
// <env name="DB_CONNECTION" value="sqlite"/>
// <env name="DB_DATABASE" value=":memory:"/>Güvenlik (Security)
Dosya İzinleri (File Permissions)
# Veritabani dosyasina sadece sahip erisebilsin
chmod 600 myapp.db
chmod 600 myapp.db-wal
chmod 600 myapp.db-shm
# Dizin izinleri (SQLite gecici dosya olusturabilmeli)
chmod 700 /path/to/db/directory/SQLCipher (Veritabanı Şifreleme / Database Encryption)
# pysqlcipher3 ile sifrelenmis veritabani
from pysqlcipher3 import dbapi2 as sqlite
conn = sqlite.connect('encrypted.db')
conn.execute("PRAGMA key='super-secret-password'")
conn.execute("PRAGMA cipher_compatibility = 4")
# Normal SQLite gibi kullan
conn.execute('CREATE TABLE secrets (id INTEGER PRIMARY KEY, data TEXT)')
conn.execute('INSERT INTO secrets (data) VALUES (?)', ('gizli bilgi',))
conn.commit()SQL Injection Korunma (SQL Injection Prevention)
# YANLIS -- string birlestirme (SQL injection acigi)
# cursor.execute(f"SELECT * FROM users WHERE name = '{user_input}'")
# DOGRU -- parameterized query
cursor.execute("SELECT * FROM users WHERE name = ?", (user_input,))
# DOGRU -- named parameters
cursor.execute(
"SELECT * FROM users WHERE name = :name AND age > :age",
{"name": user_input, "age": min_age}
)WAL Dosya Güvenliği (WAL File Security)
WAL modunda veritabanı yanında iki ek dosya oluşur:
myapp.db-wal-- Write-Ahead Log (commitlenmemiş veriler)myapp.db-shm-- Shared Memory (index bilgisi)
Bu dosyalar veritabanının bütünlüğü için kritiktir. Backup alırken üç dosya birlikte kopyalanmalıdır. Dosya izinleri ana veritabanı dosyasıyla aynı olmalıdır.
Tips
Ne Zaman SQLite Kullan, Ne Zaman Kullanma
| Senaryo | SQLite | Client-Server DB |
|---|---|---|
| Mobil uygulama | Evet | Hayır |
| Embedded / IoT | Evet | Hayır |
| Desktop uygulaması | Evet | Nadiren |
| Test/prototip | Evet | Önemli değil |
| Tek kullanıcı web | Evet | Tercih edilebilir |
| Çok kullanıcılı web | Hayır | Evet |
| Yüksek yazma trafiği | Hayır | Evet |
| Birden fazla sunucu | Hayır | Evet |
| 100+ concurrent write | Hayır | Evet |
WAL vs Journal Mode
| Özellik | Journal (DELETE) | WAL |
|---|---|---|
| Concurrent okuma | Yazma sırasında engellenir | Yazma sırasında okunabilir |
| Yazma performansı | Daha yavaş | Daha hızlı |
| Dosya sayısı | 1 (+ journal geçici) | 3 (db + wal + shm) |
| Network dosya sistemi | Destekler | Desteklemez (NFS vb.) |
| Checkpoint | Otomatik | Otomatik + manuel |
| Varsayılan | Evet | Hayır (açılması gerekir) |
Önemli PRAGMA Ayarları (Important PRAGMA Settings)
-- Her baglantida mutlaka calistirin
PRAGMA foreign_keys = ON; -- FK kisitlamalarini etkinlestir (varsayilan OFF!)
PRAGMA busy_timeout = 5000; -- Concurrent erisimde 5 sn bekle (varsayilan 0 = hemen hata)
PRAGMA journal_mode = WAL; -- Daha iyi okuma performansi
PRAGMA synchronous = NORMAL; -- WAL ile guvenli, daha hizli
-- Connection pooling gereksizdir
-- SQLite tek dosya uzerinde calisir, pool olusturmak yerine
-- her thread/request icin ayri baglanti acmak yeterlidir.
-- busy_timeout ayarlanmissa concurrent erisimde bekler.Laravel / Django / Flask ile Kullanım (Framework Usage)
# Laravel .env
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite# Django settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Flask
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///myapp.db'Limitasyonlar (Limitations)
| Limitation | Description |
|---|---|
| Concurrent writes | Tek yazıcı / Single writer at a time (WAL helps with reads) |
| ALTER TABLE | Sınırlı destek / Limited (no DROP COLUMN before 3.35.0) |
| No native ENUM | TEXT + CHECK constraint kullan |
| No RIGHT/FULL JOIN | LEFT JOIN ile workaround |
| No user management | Dosya sistemi izinleri ile kontrol / File system permissions |
| Data types | Dinamik tipleme / Dynamic typing (any type in any column) |
| Max size | Pratikte ~1 TB, sonrası yavaşlar |