19-bo‘lim
Zaxira nusxa va PHP bilan ishlash
mysqldump bilan zaxira olish, tiklash, migratsiyalar hamda PDO orqali PHP dan bazaga ulanish.
Ushbu bo‘lim mundarijasi
Baza qanchalik yaxshi loyihalangan bo'lmasin, zaxira nusxasiz u bir kunlik ish. Bu bo'limda ma'lumotni saqlash va uni ilovadan ishlatishni o'rganamiz.
mysqldump bilan zaxira olish #
# Bitta baza
mysqldump -u root -p dokon > dokon_zaxira.sql
# Bir nechta baza
mysqldump -u root -p --databases dokon maktab > ikkala.sql
# Barcha bazalar
mysqldump -u root -p --all-databases > hammasi.sql
# Faqat bitta jadval
mysqldump -u root -p dokon mahsulotlar > mahsulotlar.sql
# Faqat tuzilma (ma'lumotsiz)
mysqldump -u root -p --no-data dokon > dokon_tuzilma.sql
# Faqat ma'lumot (tuzilmasiz)
mysqldump -u root -p --no-create-info dokon > dokon_malumot.sql
Windows da mysqldump odatda shu yerda:
C:\xampp\mysql\bin\mysqldump.exe
To'liq yo'l bilan chaqiring yoki C:\xampp\mysql\bin ni PATH ga qo'shing.
Parol yo'q bo'lsa -p ni tashlab keting:
mysqldump -u root dokon > dokon_zaxira.sql
| Bayroq | Vazifasi |
|---|---|
--no-data | Faqat CREATE TABLE |
--no-create-info | Faqat INSERT |
--routines | Protsedura va funksiyalarni ham |
--triggers | Triggerlarni ham (standart yoqilgan) |
--events | Rejalashtirilgan hodisalarni ham |
--single-transaction | InnoDB uchun jadvalni bloklamasdan |
--quick | Katta jadvalni satrma-satr o'qish |
--complete-insert | Ustun nomlari bilan INSERT |
--where="..." | Faqat mos qatorlar |
# Ishlab chiqarish uchun to'liq va xavfsiz variant
mysqldump -u root -p \
--single-transaction --routines --events --triggers \
--default-character-set=utf8mb4 \
dokon > dokon_$(date +%F).sql
--single-transaction nima uchun muhim?Usiz mysqldump jadvallarni bloklaydi - sayt zaxira olinayotgan paytda
"muzlab qoladi".
Bu bayroq InnoDB uchun bir zumda suratga oluvchi tranzaksiya ochadi: sayt ishlashda davom etadi, zaxira esa bir vaqt kesimidagi izchil holatni oladi.
Tiklash #
# Bazani avval yaratish kerak
mysql -u root -p -e "CREATE DATABASE dokon CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# Tiklash
mysql -u root -p dokon < dokon_zaxira.sql
# --databases bilan olingan zaxirada CREATE DATABASE bor
mysql -u root -p < ikkala.sql
MySQL ichidan:
USE dokon;
SOURCE C:/zaxira/dokon_zaxira.sql;
Tekshirilmagan zaxira - zaxira emas.
Har oyda bir marta test serverga tiklab ko'ring:
mysql -u root -p -e "CREATE DATABASE dokon_test;"
mysql -u root -p dokon_test < dokon_zaxira.sql
mysql -u root -p dokon_test -e "SELECT COUNT(*) FROM buyurtmalar;"
Ko'p kompaniya zaxirasi buzuq ekanini faqat falokat kunida bilib qolgan.
Avtomatlashtirish #
Windows uchun oddiy skript:
@echo off
set SANA=%date:~-4%-%date:~3,2%-%date:~0,2%
set YOL=C:\zaxira
C:\xampp\mysql\bin\mysqldump.exe -u root --single-transaction ^
--routines --default-character-set=utf8mb4 ^
dokon > %YOL%\dokon_%SANA%.sql
echo Zaxira tayyor: %YOL%\dokon_%SANA%.sql
Task Scheduler orqali har kuni ishga tushiring.
Linux uchun:
#!/bin/bash
YOL=/var/backups/mysql
SANA=$(date +%F)
mysqldump --single-transaction --routines dokon | gzip > "$YOL/dokon_$SANA.sql.gz"
# 7 kundan eski fayllarni o'chirish
find "$YOL" -name "dokon_*.sql.gz" -mtime +7 -delete
# crontab -e - har kuni soat 02:00 da
0 2 * * * /usr/local/bin/zaxira.sh
Migratsiyalar #
Baza tuzilmasi ham kod kabi versiyalanishi kerak.
-- database/migrations/001_mahsulotlar.sql
CREATE TABLE mahsulotlar (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
nomi VARCHAR(150) NOT NULL,
narx DECIMAL(12,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- database/migrations/002_mahsulot_rasm.sql
ALTER TABLE mahsulotlar ADD COLUMN rasm VARCHAR(255) NULL AFTER nomi;
-- Qaysi migratsiya bajarilganini yozib boramiz
CREATE TABLE migratsiyalar (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
fayl VARCHAR(255) NOT NULL UNIQUE,
bajarilgan TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
<?php
// tools/migrate.php
$pdo = yangi_ulanish();
$bajarilgan = $pdo->query('SELECT fayl FROM migratsiyalar')
->fetchAll(PDO::FETCH_COLUMN);
$fayllar = glob(__DIR__ . '/../database/migrations/*.sql');
sort($fayllar);
foreach ($fayllar as $yol) {
$nom = basename($yol);
if (in_array($nom, $bajarilgan, true)) {
continue;
}
echo "Bajarilmoqda: {$nom}\n";
$pdo->exec(file_get_contents($yol));
$stmt = $pdo->prepare('INSERT INTO migratsiyalar (fayl) VALUES (?)');
$stmt->execute([$nom]);
}
echo "Tayyor.\n";
Bir marta ishga tushgan migratsiya o'zgarmas bo'lishi kerak. Xato bo'lsa - yangi migratsiya yozing:
-- 003_narx_turini_tuzatish.sql
ALTER TABLE mahsulotlar MODIFY narx DECIMAL(12,2) NOT NULL;
PHP va PDO #
<?php
function yangi_ulanish(): PDO
{
$host = '127.0.0.1';
$baza = 'dokon';
$kodlash = 'utf8mb4';
$dsn = "mysql:host={$host};dbname={$baza};charset={$kodlash}";
return new PDO($dsn, 'dokon_app', getenv('DB_PASSWORD'), [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
}
charset=utf8mb4 ni unutmangDSN da kodlash ko'rsatilmasa, o'zbek harflari (o', g', sh) va emoji
? belgisiga aylanib qolishi mumkin. Bu eng ko'p uchraydigan xato.
O'qish #
$pdo = yangi_ulanish();
// Barcha qatorlar
$stmt = $pdo->query('SELECT id, nomi, narx FROM mahsulotlar ORDER BY narx DESC');
$mahsulotlar = $stmt->fetchAll();
foreach ($mahsulotlar as $mahsulot) {
echo "{$mahsulot['nomi']} - {$mahsulot['narx']} so'm\n";
}
// Bitta qator
$stmt = $pdo->prepare('SELECT * FROM mahsulotlar WHERE id = ?');
$stmt->execute([$id]);
$mahsulot = $stmt->fetch();
if ($mahsulot === false) {
echo "Mahsulot topilmadi.";
}
// Bitta qiymat
$stmt = $pdo->prepare('SELECT COUNT(*) FROM buyurtmalar WHERE mijoz_id = ?');
$stmt->execute([$mijozId]);
$soni = (int) $stmt->fetchColumn();
Yozish #
// Qo'shish
$stmt = $pdo->prepare(
'INSERT INTO mahsulotlar (nomi, narx, kategoriya_id) VALUES (?, ?, ?)'
);
$stmt->execute([$nomi, $narx, $kategoriyaId]);
$yangiId = (int) $pdo->lastInsertId();
// O'zgartirish
$stmt = $pdo->prepare('UPDATE mahsulotlar SET narx = ? WHERE id = ?');
$stmt->execute([$yangiNarx, $id]);
echo $stmt->rowCount() . ' qator o\'zgardi';
// O'chirish
$stmt = $pdo->prepare('DELETE FROM mahsulotlar WHERE id = ?');
$stmt->execute([$id]);
IN bilan ishlash #
$idlar = [3, 7, 12, 18];
// Har bir qiymat uchun ? belgisi yasaymiz
$belgilar = implode(',', array_fill(0, count($idlar), '?'));
$stmt = $pdo->prepare("SELECT * FROM mahsulotlar WHERE id IN ({$belgilar})");
$stmt->execute($idlar);
$topilgan = $stmt->fetchAll();
IN (?) bitta qiymat degani// XATO - faqat massiv "Array" satriga aylanadi
$stmt = $pdo->prepare('SELECT * FROM mahsulotlar WHERE id IN (?)');
$stmt->execute([$idlar]);
Har bir element uchun alohida ? kerak. Yuqoridagi array_fill usuli
buni avtomatlashtiradi.
Massiv bo'sh bo'lsa alohida tekshiring - IN () sintaksis xatosi beradi.
Tranzaksiya #
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare(
'INSERT INTO buyurtmalar (mijoz_id, holat) VALUES (?, ?)'
);
$stmt->execute([$mijozId, 'yangi']);
$buyurtmaId = (int) $pdo->lastInsertId();
$element = $pdo->prepare(
'INSERT INTO buyurtma_elementlari (buyurtma_id, mahsulot_id, soni, narx)
VALUES (?, ?, ?, ?)'
);
$zaxira = $pdo->prepare(
'UPDATE mahsulotlar SET zaxira = zaxira - ? WHERE id = ? AND zaxira >= ?'
);
foreach ($savat as $qator) {
$element->execute([
$buyurtmaId, $qator['mahsulot_id'], $qator['soni'], $qator['narx'],
]);
$zaxira->execute([$qator['soni'], $qator['mahsulot_id'], $qator['soni']]);
if ($zaxira->rowCount() === 0) {
throw new RuntimeException('Omborda yetarli mahsulot yo\'q.');
}
}
$pdo->commit();
} catch (Throwable $xato) {
$pdo->rollBack();
error_log($xato->getMessage());
throw $xato;
}
Ko'p qatorli qo'shish #
$qatorlar = [
['Klaviatura', 250000],
['Sichqoncha', 120000],
['Monitor', 1800000],
];
$stmt = $pdo->prepare('INSERT INTO mahsulotlar (nomi, narx) VALUES (?, ?)');
$pdo->beginTransaction();
foreach ($qatorlar as [$nomi, $narx]) {
$stmt->execute([$nomi, $narx]);
}
$pdo->commit();
Har bir INSERT alohida tranzaksiya bo'lsa, MySQL har safar diskka yozadi.
1000 ta qatorni bitta tranzaksiyada qo'shish 10-50 barobar tezroq.
Sahifalash #
$sahifa = max(1, (int) ($_GET['sahifa'] ?? 1));
$chegara = 20;
$boshlash = ($sahifa - 1) * $chegara;
$stmt = $pdo->prepare(
'SELECT id, nomi, narx FROM mahsulotlar
ORDER BY id DESC
LIMIT :chegara OFFSET :boshlash'
);
$stmt->bindValue(':chegara', $chegara, PDO::PARAM_INT);
$stmt->bindValue(':boshlash', $boshlash, PDO::PARAM_INT);
$stmt->execute();
$mahsulotlar = $stmt->fetchAll();
$jami = (int) $pdo->query('SELECT COUNT(*) FROM mahsulotlar')->fetchColumn();
$sahifalarSoni = (int) ceil($jami / $chegara);
LIMIT uchun PARAM_INT shartATTR_EMULATE_PREPARES => false bo'lganda LIMIT ? ga satr uzatilsa,
MySQL LIMIT '20' deb ko'radi va sintaksis xatosi beradi.
Shuning uchun bindValue(..., PDO::PARAM_INT) ishlatiladi.
ATTR_EMULATE_PREPARES => false bo'lganda bir nom ikki marta
ishlatilishi mumkin emas:
// XATO: Invalid parameter number
$sql = 'SELECT * FROM mahsulotlar
WHERE nomi LIKE :qidiruv OR tavsif LIKE :qidiruv';
// TO'G'RI
$sql = 'SELECT * FROM mahsulotlar
WHERE nomi LIKE :qidiruv1 OR tavsif LIKE :qidiruv2';
$stmt->execute(['qidiruv1' => $matn, 'qidiruv2' => $matn]);
Bu xato aynan shu saytni qurishda ham uchragan.
Xatolarni ushlash #
try {
$stmt = $pdo->prepare('INSERT INTO mijozlar (email) VALUES (?)');
$stmt->execute([$email]);
} catch (PDOException $xato) {
if ((int) $xato->errorInfo[1] === 1062) {
echo 'Bu email allaqachon ro\'yxatdan o\'tgan.';
} else {
error_log($xato->getMessage());
echo 'Serverda xatolik yuz berdi.';
}
}
| Kod | Ma'nosi |
|---|---|
1062 | Takrorlanuvchi qiymat (UNIQUE buzildi) |
1451 | Bog'liq qatorlar bor, o'chirib bo'lmaydi |
1452 | Tashqi kalit mos kelmadi |
1146 | Jadval mavjud emas |
1054 | Ustun mavjud emas |
1045 | Kirish rad etildi (login/parol) |
Oddiy repozitoriy klassi #
<?php
class MahsulotRepozitoriy
{
public function __construct(private PDO $pdo) {}
public function toping(int $id): ?array
{
$stmt = $this->pdo->prepare('SELECT * FROM mahsulotlar WHERE id = ?');
$stmt->execute([$id]);
return $stmt->fetch() ?: null;
}
public function kategoriyaBoyicha(int $kategoriyaId): array
{
$stmt = $this->pdo->prepare(
'SELECT id, nomi, narx FROM mahsulotlar
WHERE kategoriya_id = ? AND faol = 1
ORDER BY nomi'
);
$stmt->execute([$kategoriyaId]);
return $stmt->fetchAll();
}
public function saqlang(array $malumot): int
{
$stmt = $this->pdo->prepare(
'INSERT INTO mahsulotlar (nomi, narx, kategoriya_id)
VALUES (:nomi, :narx, :kategoriya_id)'
);
$stmt->execute($malumot);
return (int) $this->pdo->lastInsertId();
}
public function ochiring(int $id): bool
{
$stmt = $this->pdo->prepare('DELETE FROM mahsulotlar WHERE id = ?');
$stmt->execute([$id]);
return $stmt->rowCount() > 0;
}
}
So'rovlarni sahifalar bo'ylab sochib tashlamang. Har bir jadval uchun bitta repozitoriy klassi yozing - keyin so'rovni optimallashtirish kerak bo'lganda qayerga qarashni bilasiz.
kutubxonabazasidanmysqldumpbilan zaxira oling.kutubxona_testbazasini yarating va zaxirani unga tiklang.- Qatorlar sonini ikkala bazada solishtiring.
--no-databilan faqat tuzilmani saqlang va faylni o'qib ko'ring.- PHP da
yangi_ulanish()funksiyasini yozing. - Kitoblarni sahifalab chiqaruvchi skript yozing (har sahifada 10 ta).
- Ijara berish amalini tranzaksiya ichida yozing:
ijaralarga qator qo'shing vakitoblar.mavjudni kamaytiring. 1062xatosini ushlab, foydalanuvchiga tushunarli xabar chiqaring.
Xulosa #
mysqldump- zaxira olishning asosiy vositasi;--single-transactionbilan sayt to'xtamaydi.- Zaxirani tiklab sinab ko'ring - tekshirilmagani zaxira hisoblanmaydi.
- 3-2-1 qoidasi: 3 nusxa, 2 xil qurilma, 1 tasi boshqa joyda.
- Tuzilma o'zgarishlarini migratsiya fayllari sifatida versiyalang.
- PDO da
ERRMODE_EXCEPTION,FETCH_ASSOCvaEMULATE_PREPARES => falseyoqing. - DSN da
charset=utf8mb4bo'lishi shart. LIMITuchunbindValue(..., PDO::PARAM_INT)ishlating.- Har bir nomlangan parametr so'rovda bir marta uchrasin.
- SQL ni repozitoriy klasslariga jamlang.
Keyingi - yakuniy bo'lim: to'liq amaliy loyiha.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.