Tuesday, April 16, 2013

Çok Sunuculu MySQL Mimarilerinde Yük Dengeleme Çözümleri

Giriş

Kurumsal uygulamaların her zaman erişilebilir ve ölçeklenebilir olmasını istiyoruz. Bunun için bilişim sistemini oluşturan her katmanda bu özellikleri sağlamamız gerekir. Bu katmanlardan biri de verileri kalıcı olarak saklamamızı ve gerektiğinde olabildiğince hızlı bir şekilde erişmemizi sağlayan ilişkisel veri tabanı sistemleridir. MySQL açık kaynak kodlu, (Oracle, MariaDB, Percona gibi) firmalardan desteğini alabileceğiniz, yaygın ve çok ölçekli kullanımı olan (Facebook gibi) bir çözüm olarak öne çıkmaktadır. MySQL'de çok sunuculu sistemler kurmak mümkündür. Çok sunuculu sistemler, yineleme (=replication) ya da MySQL kümesi (=cluster) ile kurulabilinir. Her birinin kendine göre kazanımları ve yitimleri bulunmaktadır. Bu yazının konusu, bu tür çok sunuculu MySQL sistemlerine istemcilerden ya da uygulamalardan erişimin yükü dengeleyecek şekilde nasıl sağlanabileceğidir. Bunu temel olarak iki şekilde sağlıyoruz:
  1. MySQL Proxy sunucusunu kullanmak
  2. Eğer uygulamalar Java uygulaması ise Connector/J JDBC sürücüsü kullanmak 
Birinci bölümde MySQL Proxy ve ikinci bölümde ise Connector/J çözümlerini sırayla inceleyeceğiz.

I. MySQL Proxy Kullanımı

İlk çözüm, MySQL sunucularına erişimde, istemci ile sunucu arasında vekil sunucu olarak adlandırdığımız MySQL Proxy sunucusu kullanmaktır. Vekil sunucuya çoğunlukla uygulamaların veritabanına erişiminin günlüğünü tutmak, istemcilerin yaptığı işlemlerin güvenlik amacıyla kaydını tutumak, uygulamanın başarımını ölçmek gibi görevler yükleriz. Asıl işi MySQL sunucusu yapmaktadır. Vekil sunucusu temel olarak istemcilerden gelen istekleri sunucuya yönlendirir. Bunu yaparken kendisine yüklenen sorumluluğu da yerine getirir. Vekile yük dengeleme görevi de verilebilinir. MySQL Proxy'nin en güncel sürümü 0.8.3 alpha'dır ve bu bağlantıdan indirebilirsiniz. Bu sürüm MySQL 5.0 ve sonrasındaki tüm sunucularla çalışmaktadır. 
MySQL Proxy içinden hazır olarak yük dengeleme betiği çıkmaktadır. MySQL Proxy programlama dili olarak lua'yı kullanır. Dolayısı ile yeni görevler vermek isterseniz lua dilini kullanarak kod yazmanız gerekir.
Kurulumu yaptığımızda kurulum dizininde aşağıdaki dizinler yer almaktadır:

01/15/2013  03:19 PM    <DIR>          bin
08/06/2012  01:42 PM            18,092 COPYING.txt
01/15/2013  03:19 PM    <DIR>          include
01/15/2013  03:19 PM    <DIR>          lib
01/15/2013  03:19 PM    <DIR>          licenses
08/06/2012  01:42 PM            75,713 README.txt
01/15/2013  03:19 PM    <DIR>          share
Burada bin dizininde vekil sunucusunu çalıştırmamızı sağlayacak uygulama yer alır: mysql-proxy. share\doc\mysql-proxy dizininde ise hazır kullabileceğimiz lua betikleri yer alıyor:

Directory of c:\opt32\mysql-proxy-0.8.3\share\doc\mysql-proxy

active-queries.lua        active-transactions.lua   admin-sql.lua
analyze-query.lua         auditing.lua              commit-obfuscator.lua
histogram.lua             load-multi.lua            ro-balance.lua
ro-pooling.lua            rw-splitting.lua          tutorial-basic.lua
tutorial-constants.lua    tutorial-inject.lua       tutorial-keepalive.lua
tutorial-monitor.lua      tutorial-packets.lua      tutorial-prep-stmts.lua
tutorial-query-time.lua   tutorial-resultset.lua    tutorial-rewrite.lua
tutorial-routing.lua      tutorial-scramble.lua     tutorial-states.lua
tutorial-tokenize.lua     tutorial-union.lua        tutorial-warnings.lua
xtab.lua

Proxy sunucusunu başlatmak için mysql-proxy.exe uygulamasını başlatmak gerekiyor:

cmd> mysql-proxy.exe --daemon --proxy-backend-addresses=192.168.1.1:3306 --proxy-read-only-backend-addresses=192.168.1.2:3306 --proxy-read-only-backend-addresses=192.168.1.3:3306 --proxy-lua-
script=c:\opt32\mysql-proxy-0.8.3\share\doc\mysql-proxy\rw-splitting.lua
2012-04-15 15:47:40: (critical) plugin proxy 0.8.3 started

Bu örnekte bir usta ve iki yamak MySQL sunucusunun olduğu yineleme mimarisi kullanılmıştır. Usta 192.168.1.1 nolu IP adresini, yamaklar ise 192.168.1.2 ve 192.168.1.3 nolu IP adreslerini dinlemektedir. İstemcilerinden gelen SELECT cümlelerini yamaklara ve INSERT/UPDATE/DELETE isteklerini ise ustaya yönlendiren betik rw-splitting.lua isimli dosyada yer almaktadır. Ustanın IP adresini proxy-backend-addresses parametresi ile yamakların IP adresini ise proxy-read-only-backend-addresseparametresi ile veriyoruz. MySQL Proxy 4400 numaralı portta çalışmaktadır. Şimdi sunucuya MySQL istemcisi üzerinden erişebiliriz:

cmd> mysql -uroot -proot --port 4040
Warning: Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.6.10-log MySQL Community Server (GPL)
Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> status
--------------
mysql  Ver 14.14 Distrib 5.6.10, for Win32 (x86)
Connection id:          3
Current database:       world
Current user:           root@localhost
SSL:                    Not in use
Using delimiter:        ;
Server version:         5.6.10-log MySQL Community Server (GPL)
Protocol version:       10
Connection:             localhost via TCP/IP
Server characterset:    latin1
Db     characterset:    latin1
Client characterset:    cp850
Conn.  characterset:    cp850
TCP port:               4040
Uptime:                 9 min 24 sec
Threads: 3  Questions: 18  Slow queries: 0  Opens: 70  Flush tables: 1  Open tab
les: 63  Queries per second avg: 0.031
--------------

Proxy sunucusunun SELECT cümlelerini gerçekten yamaklara gönderdiğini test etmek için Sorgu cebini açıp izleyebilirsiniz:

mysql> show status like 'Qcac%';
+-------------------------+---------+
| Variable_name           | Value   |
+-------------------------+---------+
| Qcache_free_blocks      | 1       |
| Qcache_free_memory      | 1031288 |
| Qcache_hits             | 14      |
| Qcache_inserts          | 5       |
| Qcache_lowmem_prunes    | 0       |
| Qcache_not_cached       | 11      |
| Qcache_queries_in_cache | 4       |
| Qcache_total_blocks     | 10      |
+-------------------------+---------+
8 rows in set (0.00 sec)

II. Connector/J Kullanımı

MySQL sunucuna Java uygulamasından bağlanabilmek için JDBC sürücüsü kullanıyoruz. MySQL bize Connector/J ile bu sürücüyü sağlıyor. Bu sürücünün yeteneklerinden biri de yük dengeleme yapabilmesidir. Üstelik sunucuları dinamik olarak eklemek çıkarılabilmek mümkündür. Sunucuların listesini, JDBC URL tanımında virgüllerle ayırarak veriyoruz:
jdbc:mysql:loadbalance://192.168.1.1:3306,192.168.1.2:3306,192.168.1.3:3306/world
Bu özelliğin kullanıldığı örnek uygulama kodunu aşağıda bulabilirsiniz:
1:  package com.example.test;  
2:  import java.sql.*;  
3:  /**  
4:   *  
5:   * @author Binnur Kurt  
6:   */  
7:  public class TestConnector {  
8:   public static void main(String[] args)   
9:      throws ClassNotFoundException,SQLException,InterruptedException {  
10:    Class.forName("com.mysql.jdbc.Driver");  
11:    int i = 0;  
12:    while (i < 1000) {  
13:     Connection connection = DriverManager.getConnection(  
14:      "jdbc:mysql:loadbalance://192.168.1.1:3306,192.168.1.2:3306, "+  
15:      "192.168.1.3:3306/world?"+  
16:       "loadBalanceConnectionGroup=first&loadBalanceEnableJMX=true",  
17:      "root", "root");  
18:    Statement statement = connection.createStatement();  
19:    ResultSet rs = statement.executeQuery("SELECT * FROM Country limit 10");  
20:    while (rs.next()) {  
21:      String code = rs.getString("Code");  
22:      String name = rs.getString("Name");  
23:      long population = rs.getLong("Population");  
24:      System.err.println(name + "\t" + code + "\t" + population);  
25:    }  
26:    rs.close();  
27:    connection.close();  
28:    Thread.sleep(5000);  
29:    ++i;  
30:   }  
31:   }  
32:  }  
jconsole ile uygulamaya bağlanıp com.mysql.jdbc.jmx'de tanımlı MBean'leri kullanarak yürütme zamanında yeni sunucular eklemek veya sunucu çıkarmak mümkün olabilmektedir:

1 comment:

  1. Çok güzel bir paylaşım, kodların görünümü biraz kötü duruyor umarım daha düzgün bir blog sistemine geçersiniz bizde zevkle takip ederiz. Teşekkürler.

    ReplyDelete