Documentation Home
MySQL 5.6 リファレンスマニュアル
Download this Manual
PDF (US Ltr) - 27.1Mb
PDF (A4) - 27.2Mb
HTML Download (TGZ) - 7.2Mb
HTML Download (Zip) - 7.2Mb


14.18.5.1 memcached アプリケーションに対する既存の MySQL スキーマの改変

memcached インタフェースを使用するために既存の MySQL スキーマまたはアプリケーションを改変する場合、memcached アプリケーションの次の点を検討してください。

  • スペースまたは改行文字は ASCII プロトコルで区切り文字として使用されるため、memcached のキーにこれらの文字を含めることはできません。スペースを含む検索値を使用する場合は、add()set()get() などの呼び出しでこれらをキーとして使用する前に、スペースのない値に変換またはハッシュします。これらの文字は、理論上、バイナリプロトコルを使用するプログラム内でキーとして許可されますが、さまざまなクライアントとの互換性を確保するために、キーに使用される文字を常に制限します。

  • InnoDB テーブルに短い数値の主キーカラムがある場合、整数を文字列値に変換すると、memcached の一意の検索キーとして使用できます。memcached サーバーが複数のアプリケーションで使用される場合、または複数の InnoDB テーブルとともに使用される場合、一意性を保つために名前の変更を検討してください。たとえば、数値の前にテーブル名やデータベース名とテーブル名を付けます。

    注記

    MySQL 5.6.14 以降では、InnoDB memcached プラグインは、主キーとして INTEGER が定義されたマップ済み InnoDB テーブル上での挿入および読み取りをサポートします。

  • memcached インタフェースからクエリーを実行したり格納したりしたデータについて、パーティション化されたテーブルは使用できません。

  • memcached プロトコルは数値を文字列として渡します。ベースとなる InnoDB テーブルに数値を格納する場合、たとえば、SUM()AVG() などの SQL 関数に使用できるカウンタを実装するには、次のようにします。

    • 予想される最大数のすべての桁 (さらに該当する場合はマイナス符号、小数点またはその両方に対する追加の文字) を保持するために十分な文字がある VARCHAR カラムを使用します。

    • カラム値を使用して計算を実行するすべてのクエリー内で、CAST() 関数を使用して、文字列から整数またはその他の数値型に変換します。例:

      -- Alphabetic entries are returned as zero.
      select cast(c2 as unsigned integer) from demo_test;
      -- Since there could be numeric values of 0, can't disqualify them.
      -- Test the string values to find the ones that are integers, and average only those.
      select avg(cast(c2 as unsigned integer)) from demo_test
        where c2 between '0' and '9999999999';
      -- Views let you hide the complexity of queries. The results are already converted;
      -- no need to repeat conversion functions and WHERE clauses each time.
      create view numbers as select c1 key, cast(c2 as unsigned integer) val
        from demo_test where c2 between '0' and '9999999999';
      select sum(val) from numbers;

      結果セット内のアルファベットの値は、CAST() の呼び出しによって 0 に変換されます。結果セット内の行数に依存する AVG() などの関数を使用する場合、数値以外の値を取り除くための WHERE 句を含めるようにします。

  • キーとして使用する InnoDB カラムが 250 バイトよりも長くなる可能性がある場合、250 バイト未満の値になるようにハッシュします。

  • memcached インタフェースで既存のテーブルを使用するには、そのテーブルのためのエントリを innodb_memcache.containers テーブル内に定義します。memcached を介して中継されるリクエストでテーブルをデフォルトに指定する場合、name カラムに値 default を指定し、その後 MySQL Server を再起動して変更を有効にします。異なる種類の memcached データに対して複数のテーブルを使用している場合、ユーザーの選択した name 値を使用して innodb_memcache.containers テーブル内に複数のエントリをセットアップし、次にアプリケーション内で get @@name または set @@name の形式で memcached リクエストを発行し、memcached API を経由する後続のリクエストについて使用されるテーブルを切り換えます。

    事前定義された test.demo_test テーブル以外のテーブルを使用する例については、例14.23「InnoDB + memcached アプリケーションのためのテーブルおよびカラムマッピングの指定」を参照してください。そのようなテーブルの必要なレイアウトとカラムの意味については、セクション14.18.7「InnoDB memcached プラグインの内部構造」を参照してください。

  • memcached のキー/値のペアによって複数の MySQL カラム値を使用するには、MySQL テーブルに関連付けられた innodb_memcache.containers エントリ内で、value_columns フィールドに、カンマ、セミコロン、スペース、またはパイプ文字で区切られた複数のカラム名を指定します。たとえば、col1,col2,col3 または col1|col2|col3 のようになります。

    カラム値を、パイプ文字を区切り文字として使用した単一文字列になるように連結したあとで、その文字列を memcachedadd または set 呼び出しに渡します。文字列はさまざまなカラムに自動的にアンパックされます。個々の get 呼び出しは、やはりパイプ区切り文字によって区切られている複数のカラム値を含む単一文字列を返します。アプリケーション言語に応じた適切な構文を使用して、これらの値をアンパックします。

例 14.23 InnoDB + memcached アプリケーションのためのテーブルおよびカラムマッピングの指定

ここで、データ操作のために InnoDB memcached プラグインを使用する MySQL アプリケーションのためにユーザー独自のデータを使用する方法の例を示します。

最初に、一部の国別データを保持するテーブルをセットアップします。データは、人口、メートル法で示した面積、右または左のどちら側を運転するかを示す 'R' または 'L' です。

use test;

CREATE TABLE `multicol` (
  `country` varchar(128) NOT NULL DEFAULT '',
  `population` varchar(10) DEFAULT NULL,
  `area_sq_km` varchar(9) DEFAULT NULL,
  `drive_side` varchar(1) DEFAULT NULL,
  `c3` int(11) DEFAULT NULL,
  `c4` bigint(20) unsigned DEFAULT NULL,
  `c5` int(11) DEFAULT NULL,
  PRIMARY KEY (`country`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ここで、InnoDB memcached プラグインがこのテーブルにアクセスする方法を認識するように、このテーブルのディスクリプタを作成します。

  • CONTAINERS テーブル内のサンプルエントリは、name カラムが 'aaa' で、別の識別子 'bbb' をセットアップします。使用するすべての memcached アプリケーションに対して単一のマスターテーブルを作成した場合、ID を 'default' にして、テーブルを切り換えるための @@ リクエストをスキップします。

  • test.multicol テーブルを指定します。スキーマ名は 1 つのカラムに格納され、テーブル名は別のカラムに格納されます。

  • キーカラムは一意の country 値です。このカラムは上記のテーブルを作成するときに主キーに指定されたため、ここでインデックス名 'PRIMARY' も指定します。

  • 1 つの複合データを単一カラムに保持する代わりにデータを 3 つのテーブルカラムに分割するため、値を格納または取得するときに使用されるカラムのカンマ区切りリストを指定します。

  • さらに、フラグ、期限切れ、および CAS 値については、サンプルテーブル demo.test の設定に基づく対応するカラムを指定します。これらの値は、InnoDB memcached プラグインを使用するアプリケーションでは通常はあまり重要ではありません。MySQL がデータの同期を保持するため、データが期限切れになったり古くなったりすることを心配する必要がないためです。

insert into innodb_memcache.containers
  (name,db_schema,db_table,key_columns,value_columns,flags,cas_column,
  expire_time_column,unique_idx_name_on_key)
values
  ('bbb','test','multicol','country','population,area_sq_km,drive_side',
  'c3','c4','c5','PRIMARY');

commit;

ここで、プログラムからこのテーブルにアクセスする方法を示すサンプル Python プログラムを示します。

  • すべてのデータ操作は memcached インタフェースを介して実行されるため、データベース許可は不要です。知る必要があるのは、memcached デーモンがローカルシステム上でリスニングするポート番号のみです。

  • 任意のいくつかの国のサンプル値をロードします。(ウィキペディアからの面積および人口の数値。)

  • プログラムで multicol テーブルを使用するために、@@ 表記を使用してダミーの GET または SET リクエストを行う switch_table() 関数を呼び出します。リクエスト内の名前は bbb で、これは innodb_memcache.containers.name に格納されている値です。(実際のアプリケーションでは、さらに記述的な名前を使用します。この例では、GET @@... リクエストでテーブル名でなくテーブル識別子を指定することを示しています。

  • データを挿入してクエリーを実行するためのユーティリティー関数は、ADD または SET リクエストによって MySQL に送信するために Python データ構造をパイプ区切り値に変換し、GET リクエストによって返されるパイプ区切り値をアンパックするための方法を示します。この特別な処理は、単一の memcached 値を複数の MySQL テーブルカラムにマッピングする場合にのみ必要です。

import sys, os
import memcache

def connect_to_memcached():
  memc = memcache.Client(['127.0.0.1:11211'], debug=0);
  print "Connected to memcached."
  return memc

def banner(message):
  print
  print "=" * len(message)
  print message
  print "=" * len(message)

country_data = [
("Canada","34820000","9984670","R"),
("USA","314242000","9826675","R"),
("Ireland","6399152","84421","L"),
("UK","62262000","243610","L"),
("Mexico","113910608","1972550","R"),
("Denmark","5543453","43094","R"),
("Norway","5002942","385252","R"),
("UAE","8264070","83600","R"),
("India","1210193422","3287263","L"),
("China","1347350000","9640821","R"),
]

def switch_table(memc,table):
  key = "@@" + table
  print "Switching default table to '" + table + "' by issuing GET for '" + key + "'."
  result = memc.get(key)

def insert_country_data(memc):
  banner("Inserting initial data via memcached interface")
  for item in country_data:
    country = item[0]
    population = item[1]
    area = item[2]
    drive_side = item[3]

    key = country
    value = "|".join([population,area,drive_side])
    print "Key = " + key
    print "Value = " + value

    if memc.add(key,value):
      print "Added new key, value pair."
    else:
      print "Updating value for existing key."
      memc.set(key,value)

def query_country_data(memc):
  banner("Retrieving data for all keys (country names)")
  for item in country_data:
    key = item[0]
    result = memc.get(key)
    print "Here is the result retrieved from the database for key " + key + ":"
    print result
    (m_population, m_area, m_drive_side) = result.split("|")
    print "Unpacked population value: " + m_population
    print "Unpacked area value      : " + m_area
    print "Unpacked drive side value: " + m_drive_side

if __name__ == '__main__':

  memc = connect_to_memcached()
  switch_table(memc,"bbb")
  insert_country_data(memc)
  query_country_data(memc)

  sys.exit(0)

ここに示すいくつかの SQL クエリーは、スクリプトが実行されたあとの MySQL データの状態を示し、SQL を介して同じデータに直接アクセスしたり、適切な MySQL コネクタまたは API を使用する任意の言語で記述されたアプリケーションからアクセスしたりする方法を示します。

テーブルディスクリプタ 'bbb' があるため、memcached リクエスト GET @bbb を発行すると、multicol テーブルに切り換えることができます。

mysql: use innodb_memcache;
Database changed

mysql: select * from containers;
+------+-----------+-----------+-------------+----------------------------------+-------+------------+--------------------+------------------------+
| name | db_schema | db_table  | key_columns | value_columns                    | flags | cas_column | expire_time_column | unique_idx_name_on_key |
+------+-----------+-----------+-------------+----------------------------------+-------+------------+--------------------+------------------------+
| aaa  | test      | demo_test | c1          | c2                               | c3    | c4         | c5                 | PRIMARY                |
| bbb  | test      | multicol  | country     | population,area_sq_km,drive_side | c3    | c4         | c5                 | PRIMARY                |
+------+-----------+-----------+-------------+----------------------------------+-------+------------+--------------------+------------------------+
2 rows in set (0.01 sec)

スクリプトを実行したあと、データは multicol テーブル内にあり、従来の MySQL クエリーまたは DML ステートメントから入手できます。

mysql: use test;
Database changed

mysql: select * from multicol;
+---------+------------+------------+------------+------+------+------+
| country | population | area_sq_km | drive_side | c3   | c4   | c5   |
+---------+------------+------------+------------+------+------+------+
| Canada  | 34820000   | 9984670    | R          |    0 |   11 |    0 |
| China   | 1347350000 | 9640821    | R          |    0 |   20 |    0 |
| Denmark | 5543453    | 43094      | R          |    0 |   16 |    0 |
| India   | 1210193422 | 3287263    | L          |    0 |   19 |    0 |
| Ireland | 6399152    | 84421      | L          |    0 |   13 |    0 |
| Mexico  | 113910608  | 1972550    | R          |    0 |   15 |    0 |
| Norway  | 5002942    | 385252     | R          |    0 |   17 |    0 |
| UAE     | 8264070    | 83600      | R          |    0 |   18 |    0 |
| UK      | 62262000   | 243610     | L          |    0 |   14 |    0 |
| USA     | 314242000  | 9826675    | R          |    0 |   12 |    0 |
+---------+------------+------------+------------+------+------+------+
10 rows in set (0.00 sec)

mysql: desc multicol;
+------------+---------------------+------+-----+---------+-------+
| Field      | Type                | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+-------+
| country    | varchar(128)        | NO   | PRI |         |       |
| population | varchar(10)         | YES  |     | NULL    |       |
| area_sq_km | varchar(9)          | YES  |     | NULL    |       |
| drive_side | varchar(1)          | YES  |     | NULL    |       |
| c3         | int(11)             | YES  |     | NULL    |       |
| c4         | bigint(20) unsigned | YES  |     | NULL    |       |
| c5         | int(11)             | YES  |     | NULL    |       |
+------------+---------------------+------+-----+---------+-------+
7 rows in set (0.01 sec)

数値として扱われるカラムの長さを定義する場合、必要な桁、小数点、符号文字、先行ゼロなどを保持する十分なサイズを考慮します。VARCHAR などの文字列カラム内で値が長すぎると、その値は一部の文字を削除して切り捨てられ、不適切な数値が生成されることがあります。

SQL クエリーを使用して、country キーカラムだけでなく任意のカラムを使用して計算およびテストを行うと、レポートを作成できます。(これらの例では少数の国のデータのみが使用されているため、数値は例示のみを目的としています。)ここで、右側を運転する国の平均人口と、名前がUで始まる国の平均面積を求めます。

mysql: select avg(population) from multicol where drive_side = 'R';
+-------------------+
| avg(population)   |
+-------------------+
| 261304724.7142857 |
+-------------------+
1 row in set (0.00 sec)

mysql: select sum(area_sq_km) from multicol where country like 'U%';
+-----------------+
| sum(area_sq_km) |
+-----------------+
|        10153885 |
+-----------------+
1 row in set (0.00 sec)

population および area_sq_km カラムは厳密に型指定された数値データでなく文字データを格納するため、avg()sum() などの関数は、最初にそれぞれの値を数値に変換します。この手法は <> などの演算子では機能せず、 たとえば、文字ベースの値を比較するとき、9 > 1000 となり、これは ORDER BY population DESC などの句で予期される結果ではありません。もっとも正確な型処理を行うには、数値カラムを適切な型にキャストするビューに対してクエリーを実行します。この技法では、非常に単純な SELECT * クエリーをデータベースアプリケーションから発行でき、キャスト、フィルタ、および並べ替えがすべて正しくなります。ここで、人口の上位 3 か国を降順で求めるためにクエリーを実行するビューを作成します。結果は常に multicol テーブルからの最新データを反映し、人口と面積は常に数値として扱われます。

mysql: create view populous_countries as
  select
    country,
    cast(population as unsigned integer) population,
    cast(area_sq_km as unsigned integer) area_sq_km,
    drive_side from multicol
  order by cast(population as unsigned integer) desc
  limit 3;
Query OK, 0 rows affected (0.01 sec)

mysql: select * from populous_countries;
+---------+------------+------------+------------+
| country | population | area_sq_km | drive_side |
+---------+------------+------------+------------+
| China   | 1347350000 |    9640821 | R          |
| India   | 1210193422 |    3287263 | L          |
| USA     |  314242000 |    9826675 | R          |
+---------+------------+------------+------------+
3 rows in set (0.00 sec)

mysql: desc populous_countries;
+------------+---------------------+------+-----+---------+-------+
| Field      | Type                | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+-------+
| country    | varchar(128)        | NO   |     |         |       |
| population | bigint(10) unsigned | YES  |     | NULL    |       |
| area_sq_km | int(9) unsigned     | YES  |     | NULL    |       |
| drive_side | varchar(1)          | YES  |     | NULL    |       |
+------------+---------------------+------+-----+---------+-------+
4 rows in set (0.02 sec)


User Comments
Sign Up Login You must be logged in to post a comment.