Wikipedia verilerini MySQL veritabanına daha hızlı aktarmak

Veri üzerinde analizler yapmak [1] [2] [3] veya Wikipedia’yı çevrimdışı kullanmak gibi amaçlarla Wikipedia’nın veritabanı yedeklerini zaman zaman kendi MySQL ortamımıza aktarmak istiyoruz. Fakat bu yedekler her geçen yıl büyüdüğünden, veri üzerinde çalışmadan önce harcamamız gereken zaman giderek artıyor. Örneğin İngilizce Wikipedia‘nın 2006’da sıkıştırılmamış XML hali 12 Gb’tı. Artık 2009-2010 makale yedeklerinin sıkıştırılmamış XML hali 25-30 Gb’ı buluyor. Wikipedia artık eski, daha az alan kaplayan yedekleri sunamayacak hale geldiğinden, yedek dosyalarını MySQL’e olabildiğince hızlı aktarmak önem kazanıyor.

Wikipedia yedekleri şu dosyalar halinde sunuluyor (veri indirme sayfasındaki İngilizce açıklamaları aktarıyorum):

  • pages-articles.xml.bz2 : Kategori, şablon (ing. Template), yardım (ing. Help) gibi sayfalar dahil, ana makalelerle beraber farklı Wikipedia aduzaylarından (ing. namespace) gelen sayfaları, son değiştirme bilgisiyle beraber (en son kim değiştirdi, ne zaman değiştirdi) kapsıyor. Dosya boyutunu küçültmek için geriye sadece Ana aduzayındaki makaleleri bırakacak bir filtre uygulayabilirsiniz.

Dosyanın, her bir makale için XML formatı şu şekilde:

<page>
<title>April</title>
<id>1</id>
<revision>
<id>2056597</id>
<timestamp>2010-03-11T13:37:49Z</timestamp>
<contributor>
<username> son değiştiren kişi </username>
<id>29902</id>
</contributor>
<minor />
<comment> son değiştiren kişinin yorumu (Wiki markup) </comment>
<text> makale içeriği (Wiki markup) </text>
</revision>
</page>

Aslında kendiniz içerik çözümlemesi yapmak isterseniz, ihtiyacınız olan bütün bilgi bu dosyada mevcut. Makalelerden bağlantıları, kategorileri toplayabilir, şablonları makale içine yerleştirebilir ve bir yandan da makalelerin salt yazı hallerini elde edebilirsiniz. Fakat çözümleme işlemi uzun süreceğinden pagelinks.sql.gz , categorylinks.sql.gz gibi bazı tabloların hazır hallerini indirmek daha çabuk bir çözüm.

  • redirect.sql.gz : Yönlendirme bilgileri içeriyor. rd_from yönlendiren makaleyi tanımlayan sayı (int), rd_namespace yönlendiren makalenin bulunduğu aduzayı ve rd_title yönlendirmenin hedefi olan makale başlığı. Bir tarafta sayı biçiminde makale kimliği, diğer yanda yazı şeklinde makale kimliği olunca bu tablo doğrudan çözümlemeler için basit kalıyor. Hedef makaleyi tanımlayan sayıyı kaydederek çözümlemeyi hızlandırmak için page tablosunu kullanıp bir önişlem yapmak gerekecektir.

  • pagelinks.sql.gz : Sayfalar arası bağlantıları içeriyor. Yönlendirme tablosunda olduğu gibi, burada da kaynak makale sayı ile, hedef makale yazı ile belirtiliyor. pl_from, pl_namespace,pl_title sütunlarına, yani yönlendirmeler ile aynı organizasyona sahip.


İçeriği (pages-articles.xml) Aktarmak

Wikipedia’yı çevrimdışı kullanmak veya basit, içerik tabanlı analizler yapmak için sadece pages-articles.xml.bz2 dosyasını MySQL’e aktarmak yeterli. Aktarma işlemlerinden önce, MediaWiki kurulumu yapmak doğru bir tablo düzenine sahip olmanızı sağlayacaktır. Kurulum sırasında MyISAM seçerseniz, biraz daha hızlı aktarma yapabilirsiniz. Veritabanına bir şey yazmayacağız, o yüzden analiz işleri için bunun yeterli olması gerek. Wikipedia’yı çevrimdışı olarak uzun vadeli kullanmayı düşünüyorsanız InnoDB’yi tercih etmelisiniz. Bu yazıda MyISAM kullandığınızı varsayarak devam ediyorum ancak InnoDB için de benzer adımları uygulayabilirsiniz. (Yapmanız gereken myisamchk aracıyla yaptığımız anahtar (ing. key) kapatma ve onarma işlemlerini InnoDB için gerçekleştirmek.)

pages-articles.xml.bz2 dosyası üzerinde Xml2sql programını çalıştırdığınızda, MySQL’e aktarabileceğiniz Tab karakteriyle ayrılmış page.txt, revision.txt, text.txt adında 3 dosya oluşuyor. İlk akla gelen bu dosyaları doğrudan mysqlimport kullanarak veritabanına aktarmak olacaktır. Şu şekilde:

bunzip2 -c pages-meta-current.xml.bz2 | xml2sql
mysqlimport -u root -p --local dbname `pwd`/{page,revision,text}.txt

Ancak doğrudan bu işlemi uyguladığınızda, 1-2 gün beklemeniz gerekebilir.

Onun yerine, her tabloyu aktarırken aşağıdaki adımları [4] uygulamak işin çok daha hızlı bitmesini sağlıyor. page tablosu için örnek:

sudo myisamchk --keys-used=0 -rq /var/lib/mysql/enwiki/page
(mysql komut satırı aç)
USE enwiki;
FLUSH TABLES;
ALTER TABLE page MAX_ROWS=1000000000;
LOAD DATA LOCAL INFILE 'page.txt' INTO TABLE page;
(mysql komut satırını kapat, MySQL sunucusunu kapat)
sudo myisamchk --fast --force --update-state --key_buffer_size=1024M --sort_buffer_size=1024M --read_buffer_size=64M --write_buffer_size=64M -rq /var/lib/mysql/enwiki/page

Adımları revision.txt ve text.txt dosyaları için tekrarladığınızda, elinizde kullanıma hazır bir Wikipedia veritabanı oluyor. En fazla 2 saat içinde tamamlanan bu işlemlerden sonra MediaWiki’de makaleleri görüntüleyebiliyor olmanız gerek. Veritabanıyla ilgili bazı üstveriler (ing. metadata) bu aşamada eksik olduğundan, görüntülenen makalelerin bazı bölümleri çözümlenemeyebilir.

Bağlantıları (pagelinks.sql) Aktarmak

Bağlantıları içeren pagelinks.sql dosyasının açık hali şu anda (2010) 11 Gb’ı buluyor. Doğrudan SQL çıktısı ile aktarmak için fazla büyük. Bunun yerine insert2txt programıyla bu dosyayı da – page.txt gibi fakat Tab yerine virgül karakteriyle ayrılmış – “LOAD DATA” ile yüklenebilecek biçime çeviriyoruz:

insert2txt enwiki-20080724-pagelinks.sql enwiki-20080724-pagelinks.txt

Pilho Kim‘in paylaşmış olduğu Insert2txt programı Windows için yazılmış. g++ ile derlenebilmesi ve Linux’ta çalışması için kodu modifiye edip Github’a aktardım.

Fakat daha önce kullandığımız aktarma tekniğini henüz burada kullanamıyoruz, çünkü pagelinks tablosunda “benzersiz” (ing. Unique) olarak tanımlanmış anahtarlar var. InnoDB’de SET UNIQUE_CHECKS=0 satırını kullanarak performans artışı sağlayabiliyoruz ancak MyISAM kullanırken bunu yapamıyoruz.

Bu durum için şöyle bir çözüm önerilmiş: Aynı biçimde ama benzersiz anahtarları olmayan, geçici bir tablo yaratın. Örneğin pagelinks için şu şekilde:

CREATE TABLE `tmppagelinks` (   `pl_from` int(10) unsigned NOT NULL DEFAULT '0',   `pl_namespace` int(11) NOT NULL DEFAULT '0',   `pl_title` varbinary(255) NOT NULL DEFAULT '' ) CHARSET=binary;

Bu tabloya, daha önce page.txt için yaptığımız gibi verileri aktarın. Bu sayede doğru .MYD dosyası oluşacak. Asıl pagelinks tablosundan .frm ve .MYI dosyalarını tmppagelinks tablosuna kopyalayın. MySQL komut satırında FLUSH TABLES komutunu verdikten sonra REPAIR TABLE ile olması gereken anahtarları oluşturabilirsiniz. İşlemin hızlı gerçekleşmesi için sort ve key arabellek miktarlarının az olmaması gerekiyor. Bu yüzden ben onarma işlemi sırasında sort ve key arabelleklerini artırmak için REPAIR TABLE yerine myisamchk kullanıp bellek miktarlarını argüman olarak verdim. REPAIR TABLE komutunu kullanacaksanız MySQL sunucu ayarlarınızda key_buffer_size ve sort_buffer_size değişkenlerini geçici olarak artırmanız gerekebilir. Onarma işlemi bittiğinde, eski pagelinks tablosunu DROP edip tmppagelinks tablosunu RENAME ederseniz aktarma işlemi tamamlanmış olacak. Özetle şu şekilde:

sudo myisamchk --keys-used=0 -rq /var/lib/mysql/enwiki/tmppagelinks
(mysql komut satırı aç)
USE enwiki;
FLUSH TABLES;
ALTER TABLE tmppagelinks MAX_ROWS=1000000000;
LOAD DATA LOCAL INFILE 'pagelinks.txt' INTO TABLE tmppagelinks FIELDS TERMINATED BY "," ENCLOSED BY "'" LINES TERMINATED BY "\n";
sudo cp /var/lib/mysql/enwiki/pagelinks.frm /var/lib/mysql/enwiki/tmppagelinks.frm
sudo cp /var/lib/mysql/enwiki/pagelinks.MYI /var/lib/mysql/enwiki/tmppagelinks.MYI
FLUSH TABLES;
(mysql komut satırını kapat, MySQL sunucusunu kapat)
sudo myisamchk --fast --force --update-state --key_buffer_size=1024M --sort_buffer_size=1024M --read_buffer_size=64M --write_buffer_size=64M -rq /var/lib/mysql/enwiki/tmppagelinks
(MySQL sunucusunu başlat, mysql komut satırını aç)
DROP TABLE pagelinks;
RENAME TABLE tmppagelinks TO pagelinks;

Yukarıdaki teknik sadece benzersiz (ing. Unique) anahtar tanımlanmış tablolar için gerekli. redirect gibi benzersiz anahtara sahip olmayan tablolarda, page, revision ve text tabloları için kullandığımız aktarma işlemleri yeterli.

MySQL ile ilgili Genel Öneriler

Bu bölümde MySQL ile ilgili sağda solda bulabileceğiniz bir iki genel öneriyi tekrarlamak istedim.

  • Veriyi tabloya ilk aktarmanız sırasında, anahtar tanımlamayın, olabildiğince sade bir tablo tanımlayarak veriyi ona aktarın. Bu sizi aktarım sırasında her kayıt için gerekli kontrollerden kurtarır. Daha sonra tablo yapısını değiştirip dizinleme (ing. indexing) yapabilir, anahtar tanımlayabilirsiniz. Veriyi aktardıktan sonra bu işlemleri yapmak daha hızlı olur.

  • Anahtar ara belleği (ing. key cache/key buffer) ve sıralama ara belleği (ing. sort buffer) miktarları oldukça önemli. MySQL’in öntanımlı ayarları çok eski sistemlere göre ayarlı gelmiş olabiliyor. Bellek miktarlarını dengeli bir şekilde artırın.

Karar vermek için şu bellek kullanım formülüne (tahmini bir formül) bakabilirsiniz [5]:

MySQL Toplam Bellek Kullanımı = key_buffer_size + (read_buffer_size + sort_buffer_size) * max_connections
  • Aynı şekilde, genel performans artışı için geçici tablolara (ing. tmp table size) ve sorgulara (ing. query cache size) ayrılan bellek miktarını artırabilirsiniz.

  • Filtreleme yaparken, örneğin A tablosundan, B tablosunda olmayan kayıtları silmek isterseniz DELETE FROM a WHERE NOT EXISTS (SELECT .. FROM b WHERE z) yerine CREATE TABLE atmp LIKE a ve ardından INSERT atmp SELECT * FROM a, b WHERE z komutlarını kullanarak, mevcut tablodan silmek yerine, benzer tabloya koşulu sağlayan kayıtları eklemek daha hızlı çalışabilir, denemekte fayda var. Daha sonra oluşan geçici tabloyu asıl tablo ile değiştirebilirsiniz.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Post Navigation