Archive

Archive for İ

Equality Range Optimization(eq_range_index_dive_limit system variable)

Bu mövzu şəxsi araşdırmalarımdan biridir ki, `Equality RANGE` scan zamanı 5.6 və 5.5 versiyaları arasında performance fərqinə baxacıq.
İlk öncə Equality RANGE nədir? 2 növ bizə tanış olan query təsəvvür edək:

col_name IN(val1, …, valN)
col_name = val1 OR … OR col_name = valN

Yuxarıdakı sorğular o zaman TRUE olur ki, col_name val-ların hər hansı birinə bərabər olsun. Dolayısı ilə bu müqayisə, bərabərliyi yoxlayır və məhz buna görə `Bərabərlik Aralığı`=`Equality RANGE` adlanır.
Teoriyası biraz aydın olmasa belə praktik olaraq izah edəcik bunu.
İlk öncə MySQL-imizə baxaq. MySQL 5.6.12 və MySQL 5.5.33-dir Qısa olaraq 5.6 və 5.5 olaraq qeyd edəcik.
Birinci olaraq 5.5 versiyada bu tip sorğuların necə çalışdığına baxacıq:

mysql [localhost] {msandbox} (mysqlspp) > select @@version;
+-----------+
| @@version |
+-----------+
| 5.5.33    |
+-----------+
1 row in set (0.00 sec)

Cədvəlmiz isə hər 2 tərəfdə eynidir və aşağıdakı strukturdadır:

mysql [localhost] {msandbox} (mysqlspp) > show create table sales;
+--------+------------------------------------------------------------------------------------------------------------------------------------------------+
| sales | CREATE TABLE `sales` (
  `SALES_ID` int(8) NOT NULL AUTO_INCREMENT,
  `CUSTOMER_ID` decimal(8,0) NOT NULL,
  `PRODUCT_ID` decimal(8,0) NOT NULL,
  `SALE_DATE` datetime NOT NULL,
  `QUANTITY` decimal(8,0) NOT NULL,
  `SALE_VALUE` decimal(8,0) NOT NULL,
  `DEPARTMENT_ID` decimal(8,0) DEFAULT '0',
  `SALES_REP_ID` decimal(8,0) DEFAULT '0',
  `GST_FLAG` decimal(8,0) DEFAULT NULL,
  `sale_status` char(1) DEFAULT NULL,
  `FREE_SHIPPING` char(1) DEFAULT '',
  `DISCOUNT` decimal(8,0) unsigned DEFAULT '0',
  PRIMARY KEY (`SALES_ID`),
  KEY `sales_cust_idx` (`CUSTOMER_ID`),
  KEY `PRODUCT_ID` (`PRODUCT_ID`,`CUSTOMER_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2500004 DEFAULT CHARSET=latin1 |
+--------+------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

Toplam row sayı 2500003-dir:

mysql [localhost] {msandbox} (mysqlspp) > select count(*) from sales;
+----------+
| count(*) |
+----------+
|  2500003 |
+----------+
1 row in set (14.88 sec)

Gördüyümüz kimi, cədvəldə 2500003 row var. Biraz da cədvəlimizi araşdıraq. Cədvəl statusumuza baxdıqda görürük ki, MySQL 5.5 cədvəli full scan etdikdə təqribi row sayını 2500273 hesablayır:

Bundan əlavə cədvəl index-lərinin cardinality-sini öyrənək. Bizim üçün əsas PRODUCT_ID index-idir( KEY `PRODUCT_ID`):

cardinality=200, burdan belə nəticə çıxır ki product_id təqribən 200 unique scan-la tapıla bilər.
Bu rəqəm təqribi rəqəmdir və bunu başqa yolla da tapa bilərik:

mysql [localhost] {msandbox} (mysqlspp) > select count(product_id) from sales;
+-------------------+
| count(product_id) |
+-------------------+
|           2500003 |
+-------------------+
1 row in set (0.32 sec)

mysql [localhost] {msandbox} (mysqlspp) > select count(distinct product_id) from sales;
+----------------------------+
| count(distinct product_id) |
+----------------------------+
|                        150 |
+----------------------------+
1 row in set (0.00 sec)

Burdan belə nəticə çıxardırıq ki, MySQL 5.5-də full table scan zamanı 2500273 row examine olunacaq! bundan əlavə product_id-nin seçkinliyi(cardinality) 200-dür

MySQL 5.6-da isə bu qiymətlər aşağıdakı kimidir:
Table status:

Index cardinality:

Burdan belə nəticə çıxardırıq ki, MySQL 5.6-də full table scan zamanı 2351755 row examine olunacaq!(optimizer belə qərara gəlib təbii ki bu nisbi rəqəmdir) bundan əlavə product_id-nin seçkinliyi(cardinality) 286-dır

Hər 2 versiya MySQL-də cədvəl və index-lər haqqında məlumatı əldə etdikdən sonra mövzumuzu izah etməyə başlaya bilərik. sınaqdan keçirəcəyimiz sorğu:
select count(*) from sales force index(product_id) where product_id in (1,2,3,4,5,6,7,8,9,10)

MySQL 5.5 üçün:

mysql [localhost] {msandbox} (mysqlspp) > select count(*) from sales force index(product_id) where product_id in (1,2,3,4,5,6,7,8,9,10);
+----------+
| count(*) |
+----------+
|   166659 |
+----------+
1 row in set (0.06 sec)

MySQL 5.6 üçün:

mysql> select count(*) from sales force index(product_id) where product_id in (1,2,3,4,5,6,7,8,9,10);
+----------+
| count(*) |
+----------+
|   166659 |
+----------+
1 row in set (0.05 sec)

1-dən 10-a qədər range üçün QEP MySQL 5.5-də:

mysql [localhost] {msandbox} (mysqlspp) > explain select count(*) from sales force index(product_id) where product_id in (1,2,3,4,5,6,7,8,9,10)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: sales
         type: range
possible_keys: PRODUCT_ID
          key: PRODUCT_ID
      key_len: 4
          ref: NULL
         rows: 332662
        Extra: Using where; Using index
1 row in set (0.00 sec)

MySQL 5.6-da:

mysql> explain select count(*) from sales force index(product_id) where product_id in (1,2,3,4,5,6,7,8,9,10)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: sales
         type: range
possible_keys: PRODUCT_ID
          key: PRODUCT_ID
      key_len: 4
          ref: NULL
         rows: 82220
        Extra: Using where; Using index
1 row in set (0.00 sec)

Bura diqqətlə nəzər yetirsək görərik ki, 5.5 versiya MySQL-də 332662 row scan olunur, 5.6 versiyada isə row scan sayı 82220-dir. Böyük fərq göz önündədir.

Query-imizə əlavələrlə yenidən çalışdıraq.
5.5 versiyada:

mysql [localhost] {msandbox} (mysqlspp) > explain select count(*) from sales force index(product_id) where product_id in (1,2,3,4,5,6,7,8,9,10,11,12)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: sales
         type: range
possible_keys: PRODUCT_ID
          key: PRODUCT_ID
      key_len: 4
          ref: NULL
         rows: 398890
        Extra: Using where; Using index
1 row in set (0.01 sec)

5.6 versiyada:

mysql> explain select count(*) from sales force index(product_id) where product_id in (1,2,3,4,5,6,7,8,9,10,11,12)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: sales
         type: range
possible_keys: PRODUCT_ID
          key: PRODUCT_ID
      key_len: 4
          ref: NULL
         rows: 98664
        Extra: Using where; Using index
1 row in set (0.00 sec)

398890 və 98664 yenidən fərq göz önündədir.
Lakin maraqlısı odur ki, product_id-ni 10-dan aşağı range-də müqayisə etdikdə nəticələr bərabər olur:

5.5 versiyada:

mysql [localhost] {msandbox} (mysqlspp) > explain select count(*) from sales force index(product_id) where product_id in (1,2,3,4,5,6,7,8,9)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: sales
         type: range
possible_keys: PRODUCT_ID
          key: PRODUCT_ID
      key_len: 4
          ref: NULL
         rows: 300112
        Extra: Using where; Using index
1 row in set (0.00 sec)

5.6 versiyada:

mysql> explain select count(*) from sales force index(product_id) where product_id in (1,2,3,4,5,6,7,8,9)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: sales
         type: range
possible_keys: PRODUCT_ID
          key: PRODUCT_ID
      key_len: 4
          ref: NULL
         rows: 300112
        Extra: Using where; Using index
1 row in set (0.00 sec)

Gördüyümüz kimi hər 2 halda 300112 rows examine olunacaq!
Ümumiləşdirmə aparaq, 5.6 versiyada 10-dan yuxarı range zamanı performance qazancı yaşanır lakin 10-dan aşağı range zamanı heç bir fərq baş vermir.
Buna səbəb 5.6 versiyada ilk dəfə olaraq təqdim olunmuş eq_range_index_dive_limit sistem dəyişənidir. 5.5-də bu dəyişən yoxdur:

mysql [localhost] {msandbox} (mysqlspp) > select @@eq_range_index_dive_limit;
ERROR 1193 (HY000): Unknown system variable 'eq_range_index_dive_limit'

5.6-da isə bu dəyişənin qiyməti default olaraq 10-dur:

mysql> select @@eq_range_index_dive_limit;
+-----------------------------+
| @@eq_range_index_dive_limit |
+-----------------------------+
|                          10 |
+-----------------------------+
1 row in set (0.00 sec)

Yuxarıdakı ümumiləşdirmədə göstərdiyimiz 10-nun səbəbi məhz bu dəyişənin default qiymətidir. Sınaq üçün bu dəyişənin qiymətini 9 edək və query-mizi 1-dən 9-a qədər range-lə run edək:

mysql> set @@eq_range_index_dive_limit=9;
Query OK, 0 rows affected (0.00 sec)

mysql> explain select count(*) from sales force index(product_id) where product_id in (1,2,3,4,5,6,7,8,9)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: sales
         type: range
possible_keys: PRODUCT_ID
          key: PRODUCT_ID
      key_len: 4
          ref: NULL
         rows: 73998
        Extra: Using where; Using index
1 row in set (0.00 sec)

Daha əvvəl row sayı 300112 idi, indi isə 73998 oldu. Göründüyü kimi Equality Range Optimization məqsədimizə çatmış olduq. Ən sonda eq_range_index_dive_limit dəyişəninin təsir etdiyi row sayının hesablanması alqoritmini də diqqətinizə çatdırım. Əgər xatırlayırsınızsa table status-dan row sayı bərabər idi 2351755-a. İndex cardinality isə 286-ya.
Hesablanma: 2351755/286*9 (range sayı)
Cavab:

mysql> select 2351755/286*9;
+---------------+
| 2351755/286*9 |
+---------------+
|    74006.2762 |
+---------------+
1 row in set (0.03 sec)

Həqiqətən də 74006.2762 təqribən bərabərdir 73998-a.
Nəticə olaraq onu deyə bilərik ki, MySQL 5.6 özü ilə bərabər bir çox yeniliklər gətirmişdir. bu yeniliklər əsasə etibarı ilə 3 qrupa bölünür:
1. Replication( GTİD based and crash-safe)
2. İnnoDB (performance and safety optimization)
3. MySQL query optimizer improvements

Bu tip yeniliklərdən istifadə edə bilməniz üçün 5.6 versiyaya upgrade etməniz məsləhətdir.

Təşəkkürlər 😉

MySQL timestamp və datetime tiplərinin optimal şəkildə istifadəsi

Bu mövzu xüsusən web developer-lərə faydalı olacaq deyə düşünürəm.

MySQL timestamp tipindən gözəl şəkildə faydalanmaqdan söhbət açmışıq.

Installing Django on virtualenv (CentOS 6.4)

Bu tutorial-ı izlərkən edilən addımları təkrarlamağa çalışdım lakin bir çox problemlər çıxdı ki, onları qeyd edirəm bəlkə kiməsə yardımçı olar.
Ardıcıllığı ilə:

[root@localhost ~]# easy_install virtualenv
Searching for virtualenv
Reading https://pypi.python.org/simple/virtualenv/
Download error on https://pypi.python.org/simple/virtualenv/: unknown url type: https -- Some packages may not be found!
Couldn't find index page for 'virtualenv' (maybe misspelled?)
Scanning index of all packages (this may take a while)
Reading https://pypi.python.org/simple/
Download error on https://pypi.python.org/simple/: unknown url type: https -- Some packages may not be found!
No local packages or download links found for virtualenv

Bunun həllini virtualenv-i paket şəklində download edib install etməklə tapdım:

[root@localhost virtualenv-1.9]# python setup.py install
running install
.
.
Installed /usr/local/lib/python2.7/site-packages/virtualenv-1.9-py2.7.egg
Processing dependencies for virtualenv==1.9
Finished processing dependencies for virtualenv==1.9

virtualenv yaradırıq:

[root@localhost ~]# virtualenv --no-site-packages django-sh
New python executable in django-sh/bin/python
Installing setuptools............done.
Installing pip...............done.

Aktivləşdiririk:

[root@localhost ~]# source django-sh/bin/activate
(django-sh)[root@localhost ~]# cd django-sh/

Və Django-nu install etməyə çalışırıq:

(django-sh)[root@localhost django-sh]# easy_install Django
Searching for Django
Reading http://pypi.python.org/simple/Django/
Download error: unknown url type: https -- Some packages may not be found!
Couldn't find index page for 'Django' (maybe misspelled?)
Scanning index of all packages (this may take a while)
Reading http://pypi.python.org/simple/
Download error: unknown url type: https -- Some packages may not be found!
No local packages or download links found for Django
Best match: None

Bir başqa üsulla sınayırıq:

(django-sh)[root@localhost bin]# pip install django
.
.
class VerifiedHTTPSConnection(httplib.HTTPSConnection):
AttributeError: 'module' object has no attribute 'HTTPSConnection'

Alınmadı. Həll yolu kimi aşağıdakıları etdim:

[root@localhost ~]# yum install openssl openssl-devel
.
.
Installed:
  openssl-devel.x86_64 0:1.0.0-27.el6_4.2                                       

Dependency Installed:
  keyutils-libs-devel.x86_64 0:1.4-4.el6                                        
  krb5-devel.x86_64 0:1.10.3-10.el6_4.4                                         
  libcom_err-devel.x86_64 0:1.41.12-14.el6_4.2                                  
  libselinux-devel.x86_64 0:2.0.94-5.3.el6_4.1                                  
  libsepol-devel.x86_64 0:2.0.41-4.el6                                          

Complete!

Daha sonra Python 2.7-ni yenidən build etdim:

[root@localhost Python-2.7.5]# ./configure -with-zlib=/usr/include
[root@localhost Python-2.7.5]# make
[root@localhost Python-2.7.5]# make install

Və bir daha sınayaq:

(django-sh)[root@localhost bin]# pip install django
Downloading/unpacking django
Downloading Django-1.5.2.tar.gz (8.0MB): 8.0MB downloaded
Running setup.py egg_info for package django
.
.
Successfully installed django
Cleaning up...

Alındı 🙂
Ardınca yeni Django project yaradıb test edirik. Bunlar da Tutorial-da qeyd olunur:

(django-sh)[root@localhost bin]# django-admin.py startproject django_test
(django-sh)[root@localhost bin]# ls
activate       activate_this.py  easy_install      pip-2.7  python2.7
activate.csh   django-admin.py   easy_install-2.7  python
activate.fish  django_test       pip               python2
(django-sh)[root@localhost bin]# cd django_test/
(django-sh)[root@localhost django_test]# ls
django_test  manage.py

İlk django_test adlı proyektimizi yaratdıq. test üçün server-i run edek:

(django-sh)[root@localhost django_test]# python manage.py runserver
Validating models...

0 errors found
August 15, 2013 - 15:48:16
Django version 1.5.2, using settings 'django_test.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[15/Aug/2013 15:48:22] "GET / HTTP/1.1" 200 1962

http://127.0.0.1:8000/-ə gedərək sevinirik:

Kateqoriyalar: Linux, PHP/Python Etiketlər:

Installing Python 2.7.x and 3.3.x on CentOS 6.4

Avqust 14, 2013 2 şərh

2 versiya Python istifadə etməyimiz üçün onları ayrı-ayrı qurmaq lazımdır.
Ardıcıllıqla:

[root@localhost ~]# yum install gcc make
.
.
Installed:
  gcc.x86_64 0:4.4.7-3.el6                                                      

Dependency Installed:
  cloog-ppl.x86_64 0:0.15.7-1.2.el6                                             
  cpp.x86_64 0:4.4.7-3.el6                                                      
  glibc-devel.x86_64 0:2.12-1.107.el6_4.2                                       
  glibc-headers.x86_64 0:2.12-1.107.el6_4.2                                     
  kernel-headers.x86_64 0:2.6.32-358.14.1.el6                                   
  mpfr.x86_64 0:2.4.1-6.el6                                                     
  ppl.x86_64 0:0.10.2-11.el6                                                    

Complete!

Python-nun istədiyimiz versiyasını download edirik. İlk öncə 2.7 versiyanın qurulumu:

[root@localhost Downloads]# cd Python-2.7.5
[root@localhost Python-2.7.5]# ls
config.guess  Doc         LICENSE          Objects        Python
config.sub    Grammar     Mac              Parser         README
configure     Include     Makefile.pre.in  PC             RISCOS
configure.ac  install-sh  Misc             PCbuild        setup.py
Demo          Lib         Modules          pyconfig.h.in  Tools

[root@localhost Python-2.7.5]# ./configure
.
.
creating Modules/Setup
creating Modules/Setup.local
creating Makefile

[root@localhost Python-2.7.5]# make
[root@localhost Python-2.7.5]# make install

Və yoxlayaq:

[root@localhost ~]# python
Python 2.7.5 (default, Aug 15 2013, 21:36:59) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> exit()

Python 3.3 versiya üçün:

[root@localhost Python-3.3.2]# ./configure
[root@localhost Python-3.3.2]# make
.
.
gcc -pthread   -Xlinker -export-dynamic -o Modules/_testembed Modules/_testembed.o libpython3.3m.a -lpthread -ldl  -lutil   -lm

[root@localhost Python-3.3.2]# make install

Və yoxlayaq:

[root@localhost ~]# python3
Python 3.3.2 (default, Aug 15 2013, 21:57:56) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

Təbriklər 😉

Kateqoriyalar: Linux, PHP/Python Etiketlər:

Partial Data Recovery(restoring .ibd file)

Mövzumuz advance olub fiziki olaraq data-nın recover olunması haqqındadır. Qeyd edək ki bu yalnız MySQL 5.6.x versiyalar üçün geçərlidir.
Biraz teorik olaraq məsələni izah edək. MySQL-də cədvəllər database daxilində yerləşir. Cədvəlin öz strukturu .frm faylında, cədvəlin index və data-ları isə .ibd faylında yerləşir.
.ibd cədvəlin tablespace-dir. Və bu hər cədvəl üçün ayrılıqda yaradılır(Əgər MySQL innodb_file_per_table=1 olaraq start olunubsa)
Qeyd edək ki, əgər MySQL innodb_file_per_table=1 şəklində start OLUNMAYIBSA, cədvəlin bütün index və dataları system tablepspace-də yer alır. system tablespace isə shared tablespace olduğu üçün biz onun daxilindən yalnız 1 cədvəlin taplespace-ini çıxardıb restore edə bilmərik. Yəni partial(ayrıca cədvəl) recovery üçün ilkin şərt innodb_file_per_table=1-dir.
system tablespace haqqında qısa məlumat:
A small set of data files (the ibdata files) containing the metadata for InnoDB-related objects (the data dictionary), and the storage areas for the undo log, the change buffer, and the doublewrite buffer. Depending on the setting of the innodb_file_per_table, when tables are created, it might also contain table and index data for some or all InnoDB tables. The data and metadata in the system tablespace apply to all the databases in a MySQL instance.

Bütün bunları nəzərə alıb MySQL-imizi bütün proyektlərimizdə innodb_file_per_table=1 ilə start edirik.
Bunu əyani olaraq göstərək. Database və cədvəl yaradaq:

mysql> create database blog;
Query OK, 1 row affected (0.09 sec)

mysql> use blog;
Database changed
mysql> create table partial_test(id int not null);
Query OK, 0 rows affected (0.36 sec)

mysql> insert into partial_test() values(1),(2),(3),(4),(5);
Query OK, 5 rows affected (0.27 sec)
Records: 5  Duplicates: 0  Warnings: 0

Və yoxlayaq:

[root@localhost ~]# cd /var/lib/mysql/blog
[root@localhost blog]# ls
db.opt  partial_test.frm  partial_test.ibd

Gördüyümüz kimi .frm və .idb faylları yaradılmışdır. Və bu da onu göstərir ki biz partial restore edə bilərik.

Bizim cədvəlimizdə olan məlumatlar:

mysql> select * from partial_test;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

Və biz çox ənənəvi hal kimi .ibd faylını başqa bir direktoriyaya copy edək:

[root@localhost blog]# cp partial_test.ibd /home/sh

[root@localhost sh]# ls | grep partial*
partial_test.ibd

İndi də cədvəlimizi səhvən, çox kədərli hal kimi truncate edək:

mysql> truncate table partial_test;
Query OK, 0 rows affected (0.35 sec)

mysql> select * from partial_test;
Empty set (0.00 sec)

İddia edirik ki, əgər kopyaladığımız və içində datası olan .ibd file-ı geri qaytardıqda bizim cədvəlimizdə itirilmiş məlumatlar da geri dönəcək. Sınayaq.
Shutting down MySQL:

root@localhost sh]# service mysql stop
Shutting down MySQL....                                    [  OK  ]

Geri kopyalama:

[root@localhost sh]# cp partial_test.ibd /var/lib/mysql/blog
cp: overwrite `/var/lib/mysql/blog/partial_test.ibd'? y

MySQL start:

[root@localhost sh]# service mysql start
Starting MySQL.                                            [  OK  ]

Connect oluruq və yoxlayırıq və nə pis ki, xəyallar boşa çıxdı:

mysql> select * from partial_test;
ERROR 1146 (42S02): Table 'blog.partial_test' doesn't exist

Və ERROR log-a baxdıqda biz görürük:

InnoDB: Error: table ‘blog/partial_test’
InnoDB: in InnoDB data dictionary has tablespace id 14,
InnoDB: but a tablespace with that id does not exist. There is
InnoDB: a tablespace of name blog/partial_test and id 13, though. Have
InnoDB: you deleted or moved .ibd files?
.
.
.
[ERROR] InnoDB: Failed to find tablespace for table ‘”blog”.”partial_test”‘ in the cache. Attempting to load the tablespace with space id 14.
.
.
.
InnoDB: cannot calculate statistics for table “blog”.”partial_test” because the .ibd file is missing.

Dolayısı ilə artıq bizim cədvəl itirilmişdir(çox təəssüf). Bu da physical copy alıb backup ümidi ilə yaşayanlara kiçik bir dərs olsun.
Bir daha etdiklərimizi ümumiləşdirsək, biz cədvəl yaratdıq, məlumat daxil etdik, daxil edilmiş məlumatlı .ibd faylımızı başqa bir direktoriyaya kopyaladıq. Daha sonra cədvəlimizi truncate etdik və .idb faylını geri kopyaladıq o niyyətlə ki, data-lar geri qayıtsın. NƏTİCƏ= alınmadı.
Davam edək cədvəlimizi drop edək:

mysql> drop table partial_test;
Query OK, 0 rows affected (0.17 sec)

Lakin nə möcüzədirsə partial_test cədvəli-nin tablespace-i silinmir(discard olunmur):

[root@localhost blog]# ls
db.opt  partial_test.ibd

ERROR log-a baxdıqda:
2013-08-10 13:39:27 7fc7c69b6700 InnoDB: Operating system error number 2 in a file operation.
InnoDB: The error means the system cannot find the path specified.

İnnoDB: cannot calculate statistics for table “blog”.”partial_test” because the .ibd file is missing.

Hətta düzgün drop da getmədi. Məcburən:

[root@localhost blog]# rm partial_test.ibd
rm: remove regular file `partial_test.ibd'? y
[root@localhost sh]# service mysql stop
Shutting down MySQL....                                    [  OK  ]
[root@localhost sh]# service mysql start
Starting MySQL.                                            [  OK  ]

2-ci ssenarimizi yaradaq. Cədvəl truncate etmək əvəzinə gəlin .ibd faylın özünü bir-başa silək və geri kopyalayaq:

mysql> create table remove_test(id int not null);
Query OK, 0 rows affected (0.32 sec)

mysql> show tables;
+----------------+
| Tables_in_blog |
+----------------+
| remove_test    |
+----------------+
1 row in set (0.00 sec)

mysql> insert into remove_test() values(1),(2),(3),(4),(5);
Query OK, 5 rows affected (0.09 sec)
Records: 5  Duplicates: 0  Warnings: 0

Və kopyalayaq:

[root@localhost blog]# ls
db.opt  remove_test.frm  remove_test.ibd
[root@localhost blog]# cp remove_test.ibd /home/sh

Daha sonra təsəvvür edək ki, yeni məlumatlar daxil edilib və index əlavə olunub:

mysql> alter table remove_test add index(id);
mysql> insert into remove_test() values(6),(7),(8),(9),(10);
Query OK, 5 rows affected (0.10 sec)
Records: 5  Duplicates: 0  Warnings: 0

Düşünün ki, kimsə server işlək vəziyyətdə ola-ola remove_test.ibd faylını datadir-dən remove edir:

[root@localhost blog]# rm remove_test.ibd
rm: remove regular file `remove_test.ibd'? y

Bizim bundan dərhal xəbərimiz olmayacaq. Lakin bir müddət sonra server-ə restart verdikdə və daha sonra baxdıqda:

mysql> select * from remove_test;
ERROR 1146 (42S02): Table 'blog.remove_test' doesn't exist

ERROR log-a nəzər yetirdikdə:

[ERROR] InnoDB: Could not find a valid tablespace file for ‘blog/remove_test’
[ERROR] InnoDB: Tablespace open failed for ‘”blog”.”remove_test”‘, ignored.

İndi isə server-i shut edib kopyaladığımız .ibd faylını geri gətirək. Məqsəd heç olmasa data-larımızın yarısını geri qaytarmaqdır:

[root@localhost sh]# cp remove_test.ibd /var/lib/mysql/blog
[root@localhost sh]# chown -R mysql:mysql /var/lib/mysql
[root@localhost blog]# ls
db.opt  remove_test.frm  remove_test.ibd

Start edirik və hətta MySQL-imiz crash olur…
Bunu mən BUG kimi report etdim artıq:
#69980
Bunun səbəbi dokumentasiyaya əks getməyimizdir 🙂

You cannot freely move .ibd files between database directories as you can with MyISAM table files. The table definition stored in the InnoDB shared tablespace includes the database name. The transaction IDs and log sequence numbers stored in the tablespace files also differ between databases.

Dolayısı ilə yenidən fiziki olaraq cədvəlləri kopyalayanların kədərini artıracaq bir hadisə baş verdi ki, bu tip “backup” backup deyildir.

Ümumi yuxarıda qeyd olunanlardan çıxan nəticələr:
1)
Heç bir şəkildə əməliyyat sisteminin köməyi ilə cədvəllərə dəyişiklik etməyin, onları silməyin.
Yəni rm qəbul olunmazdır(yuxarıda əyani nümunə var)
2)
Fiziki olaraq .ibd fayllarının kopyalanması sizə recover edə biləcəyiniz bir backup vermir.Yenə də yuxarıda göstərdiyimiz 2 hal buna sübutdur.

Bəs sual olunsun ki, partial restore-u necə edə bilərik?
Bunun üçün yeni cədvəl yaradaq onun düzgün backup-ını alaq və düzgün restore-unu göstərək.
Ardıcıllığa riayət etməyiniz mütləqdir:

mysql> create database blog;
Query OK, 1 row affected (0.06 sec)

mysql> use blog;
Database changed

mysql> create table restore_it(id int not null);
Query OK, 0 rows affected (0.46 sec)

mysql> insert into restore_it() values(1),(2),(3),(4),(5);
Query OK, 5 rows affected (0.33 sec)
Records: 5  Duplicates: 0  Warnings: 0

Düzgün physical backup-ı biz XtraBackup(innobackupex) [online hot backup tool] ilə alırıq. Bu bizə “safe”=”clean” .ibd file verəcək. Yəni tam dokumentasiyada göstərildiyi kimi:
In this context, a “clean” .ibd file backup is one for which the following requirements are satisfied:

There are no uncommitted modifications by transactions in the .ibd file.

There are no unmerged insert buffer entries in the .ibd file.

Purge has removed all delete-marked index records from the .ibd file.

mysqld has flushed all modified pages of the .ibd file from the buffer pool to the file.

Backup alaq:

[root@localhost ~]# innobackupex --user=root --password=12345 /home/sh/backup5 --no-timestamp --defaults-file=/usr/my.cnf
.
.
.
130811 00:54:21  innobackupex: Connection to database server closed
130811 00:54:21  innobackupex: completed OK!

Backup-dan sonra işimizi çətinləşdirmək məqsədilə index əlavə edək:

mysql> alter table restore_it add index(id);
Query OK, 0 rows affected (0.62 sec)
Records: 0  Duplicates: 0  Warnings: 0

Daha sonra cədvəlimizi truncate edək ki, restore-a səbəb olsun 😉

mysql> truncate table restore_it;
Query OK, 0 rows affected (0.50 sec)

Restore üçün physical backup-ımızı prepare etməliyik:

[root@localhost ~]# innobackupex --apply-log --redo-only /home/sh/backup5
.
.
.
InnoDB: Starting shutdown...
InnoDB: Shutdown completed; log sequence number 1679817
130811 01:07:06  innobackupex: completed OK!

Və:
[root@localhost ~]# innobackupex --apply-log /home/sh/backup5
.
.
InnoDB: Shutdown completed; log sequence number 1679904
130811 01:07:29  innobackupex: completed OK!

Bizə full restore gərəkli olmadığına görə. Yəni biz bütün database-i yox, məhz 1 cədvəli geri qaytarmaq istədiyimiz üçün. İnnobackupex-in copy-back funksiyasından yox, məhz MySQL-in öz imkanlarından istifadə edəcik. Xahiş olunur ən əhəmiyyətli hissəyə diqqət yetirək ani səhv yenə də cədvəlimizin itirilməsinə gətirib çıxardacaq:
1-ci addım köhnə tablespace-i discard edirik:

mysql> alter table restore_it discard tablespace;
Query OK, 0 rows affected (0.08 sec)

2-ci addım İnnobackupex-lə hazırladımız backup olan direktory-dən .ibd faylını kopyalamaq:

[root@localhost ~]# cd /home/sh/backup5
[root@localhost blog]# cp restore_it.ibd /var/lib/mysql/blog
[root@localhost blog]# chown -R mysql:mysql /var/lib/mysql

3-cü addım yeni tablespace-i import edirik:

mysql> alter table restore_it import tablespace;
Query OK, 0 rows affected, 2 warnings (0.38 sec)

Bu zaman warning-lər çıxır:

mysql> show warnings;
+---------+------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| Level   | Code | Message                                                                                                                                         |
+---------+------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| Warning | 1810 | InnoDB: IO Read error: (2, No such file or directory) Error opening './blog/restore_it.cfg', will attempt to import without schema verification |
| Warning | 1817 | InnoDB: Index corrupt: Index '"id"' not found or corrupt, you should recreate this index. 

restore_it.cfg faylına hələ ki nəzər yetirmirik. Bu haqda ayrıca yazım olacaq.
2-ci Warning index-in tapılmadığını ve bizim onu yenidən yaratmalı olduğumuzu bildirir.

4-cü addım data-nın qayıdıb-qayıtmadığını yoxlamaqdır:

mysql> select * from restore_it;
ERROR 1712 (HY000): Index restore_it is corrupted

Köhnə index-i drop edək:

mysql> alter table restore_it drop index id;
Query OK, 0 rows affected (0.28 sec)
Records: 0  Duplicates: 0  Warnings: 0

Və mutlu sonu görək:

mysql> select * from restore_it;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

🙂 Məlumatlarımız geri döndü.

Partial restore üçün lazım olanları ümumiləşdirsək:
*) innodb_file_per_table=1 ilə start olmuş server.
*) Xtrabackup-la alınmış clean physical backup
*) 1 ədəd MySQL-i sevən beyin

Təşəkkürlər.

Installing Xtrabackup 2.1.3 on CentOS 6.4

Ən əsas backup tool-lardan olan Xtrabackup-ın Centos 6.4-ə qurulumu zamanı yaşanan bəzi xırda çətinliklərdən dolayı bu yazını yazdım:

Xtrabackup-ı aşağıdakı linkdən yükləyə bilərsiniz:
Xtrabackup

İnstall etmək istədikdə:

[root@localhost Downloads]# rpm -ivh percona-xtrabackup-2.1.3-608.rhel6.x86_64.rpm
warning: percona-xtrabackup-2.1.3-608.rhel6.x86_64.rpm: Header V4 DSA/SHA1 Signature, key ID cd2efd2a: NOKEY
error: Failed dependencies:
	perl(DBD::mysql) is needed by percona-xtrabackup-2.1.3-608.rhel6.x86_64
	perl(Time::HiRes) is needed by percona-xtrabackup-2.1.3-608.rhel6.x86_64

Adətən perl modullarını cpan ilə install edirik:

[root@localhost Downloads]# cpan DBD::mysql
.
.
.
Running make install
  Make had returned bad status, install seems impossible

Dolayısı ilə başqa yolu sınamağa məcburuq:

[root@localhost Downloads]# yum install perl-DBD-MySQL
.
.
.Installed:
  perl-DBD-MySQL.x86_64 0:4.013-3.el6                                           

Dependency Installed:
  perl-DBI.x86_64 0:1.609-4.el6                                                 

Complete!

Daha sonra da digər dependency:

[root@localhost Downloads]# yum install perl-Time-HiRes
.
.
.
Installed:
  perl-Time-HiRes.x86_64 4:1.9721-131.el6_4                                     

Complete!

Son mərhələ:

[root@localhost Downloads]# rpm -ivh percona-xtrabackup-2.1.3-608.rhel6.x86_64.rpm
warning: percona-xtrabackup-2.1.3-608.rhel6.x86_64.rpm: Header V4 DSA/SHA1 Signature, key ID cd2efd2a: NOKEY
Preparing...                ########################################### [100%]
   1:percona-xtrabackup     ########################################### [100%]


[root@localhost Downloads]# xtrabackup --version
xtrabackup version 2.1.3 for Percona Server 5.1.59 unknown-linux-gnu (x86_64) (revision id: 608)

MySQL Foreign keys (tutorial N3)

Bu video tutorialda MySQL Foreign key-dən istifadənin üstünlüklərindən danışmışıq.
Xüsusilə developer-lərə maraqlı olacaq bir video-dur.

Level: intermediate

Kateqoriyalar: MySQL Video Tutorials Etiketlər: