Oktober 2010 Archive

Es gibt ja diesen wunderschönen Begriff der christlich-jüdisch-abendländischen Kultur.
Ich will ja nicht darüber schwadronieren zu welcher Morgenstunde diese Religionen das Tageslicht erblickten.
Nur würde ich als Muslim(a) mir sorgen machen, irgendwann doch dazu zu gehören. Denn es steht außer Frage, dass bei christlich-jüdisch-muslimisch-abendländische Kultur. Das Muslimische (die Träger dieses Bezeichners), als Juniorpartner einsteigen würden. Gerade die christlich-jüdische Geschichte im Blickpunkt habend, ist dies keine Option!

*gg* 
Q4M (Queue for MySQL) ist ein StorageEngine (SE-Plugin) für MySQL, welches sich der sicheren Übertragung und asynchronem Queing von Nachrichten (Rows) verschrieben hat. Queuing selbst erfährt spätestens seitdem es auch eine NoSQL-Implementierung (RabbitMQ) gibt, den entsprechenden Hype:)
Selbst hatte ich noch nicht das Vergnügen eine Verwendung dafür zu haben. Wer schöne Verwendungsbeispiele hat bitte posten.
Die offizielle Installation werde ich  vereinfachen und hier auf ein Beispiel für MySQL(5.1.41-3ubuntu12.6) auf Ubuntu runterbrechen. Genaugenommen benöten wir nur das SE-Plugin, welche wir in das plugin_dir unserer Installation kopieren müßen.

mysql> select VARIABLE_VALUE from information_schema.GLOBAL_VARIABLES WHERE VARIABLE_NAME='plugin_dir';
+-----------------------+
| VARIABLE_VALUE        |
+-----------------------+
| /usr/lib/mysql/plugin |
+-----------------------+
1 row in set (0.00 sec)

Von der Website holen wir das passende tar-File. Aus diesem brauchen wir nur die libqueue_engine.so welche nach plugin_dir kopiert wird.

> wget http://q4m.31tools.com/dist/old/mysql-5.1.41-linux-x86_64-glibc23-with-fast-mutexes-q4m-0.8.9.tar.gz
> tar xfz mysql-5.1.41-linux-x86_64-glibc23-with-fast-mutexes-q4m-0.8.9.tar.gz
> cp q4m-0.8.9-linux-x86_64/libqueue_engine.so /usr/lib/mysql/plugin

Nach Installation ist noch die Datei install.sql auszuführen. install.sql hat folgenden Inhalt.

INSTALL PLUGIN queue SONAME 'libqueue_engine.so';
CREATE FUNCTION queue_wait RETURNS INT SONAME 'libqueue_engine.so';
CREATE FUNCTION queue_end RETURNS INT SONAME 'libqueue_engine.so';
CREATE FUNCTION queue_abort RETURNS INT SONAME 'libqueue_engine.so';
CREATE FUNCTION queue_rowid RETURNS INT SONAME 'libqueue_engine.so';
CREATE FUNCTION queue_set_srcid RETURNS INT SONAME 'libqueue_engine.so';

Neben der Installation des Plugins (erste Zeile), werden nocht die UDFs queue_wait(), queue_end(), queue_abort(), queue_rowid() und queue_set_srcid() installiert. Zur Kontrolle:

mysql> show plugins;
+------------+----------+----------------+--------------------+---------+
| Name       | Status   | Type           | Library            | License |
+------------+----------+----------------+--------------------+---------+
| binlog     | ACTIVE   | STORAGE ENGINE | NULL               | GPL     |
| partition  | ACTIVE   | STORAGE ENGINE | NULL               | GPL     |
| ARCHIVE    | ACTIVE   | STORAGE ENGINE | NULL               | GPL     |
| BLACKHOLE  | ACTIVE   | STORAGE ENGINE | NULL               | GPL     |
| CSV        | ACTIVE   | STORAGE ENGINE | NULL               | GPL     |
| FEDERATED  | DISABLED | STORAGE ENGINE | NULL               | GPL     |
| MEMORY     | ACTIVE   | STORAGE ENGINE | NULL               | GPL     |
| InnoDB     | ACTIVE   | STORAGE ENGINE | NULL               | GPL     |
| MyISAM     | ACTIVE   | STORAGE ENGINE | NULL               | GPL     |
| MRG_MYISAM | ACTIVE   | STORAGE ENGINE | NULL               | GPL     |
| QUEUE      | ACTIVE   | STORAGE ENGINE | libqueue_engine.so | GPL     |
+------------+----------+----------------+--------------------+---------+

mysql> select * from mysql.func;
+-----------------+-----+--------------------+----------+
| name            | ret | dl                 | type     |
+-----------------+-----+--------------------+----------+
| queue_wait      |   2 | libqueue_engine.so | function |
| queue_end       |   2 | libqueue_engine.so | function |
| queue_abort     |   2 | libqueue_engine.so | function |
| queue_rowid     |   2 | libqueue_engine.so | function |
| queue_set_srcid |   2 | libqueue_engine.so | function |
+-----------------+-----+--------------------+----------+

Trickfrage: Warum wird Q4M nicht funktionieren, wenn MySQLd mit skip-grant-tables gestartet wird? :)

Nun sind Tabellen mit der Engine Queue erstellbar. Jede dieser Tabelle stellt eine Queue dar. Wobei die SE Queue weder Indizes, noch UPDATEs oder REPLACEs beherrscht. Auch binlog aka Replikation wird nicht unterstützt.
Rows/Nachrichten werden durch gewohnte INSERTs in die Tabelle(n) geschrieben. Die installierten UDFs bringen die Abfrage in den Queue(Owner)-Modus. In diesem wird sicher gestellt, dass eine Nachricht welche abgholt - queue_wait() - wird von dem queue_wait() einer anderen Verbindung nicht mehr gesehen wird und die Abholgung endgültig vollzogen (rows_removed) ist, wenn ein weiteres queue_wait() oder  queue_end() abgesetzt wird.
Ein queue_abort() oder sonst ein Abbruch der Verbindung beläßt die Nachricht/Row in der Queue/Tabelle und macht diese für die nächste Abfrage wieder sichtbar.

1.client> create table message_q (num int not null, poster varchar(255), message varchar(255)) engine=queue;
Query OK, 0 rows affected (0.03 sec)

1.client> insert into message_q values(1,'mama','komm essen'),(2,'papa','pflanze einen baum'),(3,'chef','%&#%$'),(4,'kind','spielen');
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

Wir reservieren uns im ersten Client die erste Zeile der Queue:

1.client> select * from message_q where queue_wait('message_q');
+-----+--------+------------+
| num | poster | message    |
+-----+--------+------------+
|   1 | mama   | komm essen |
+-----+--------+------------+
1 row in set (0.00 sec)

Wir haben die erste Nachricht und sagen der Engine, dass wir diese erhalten haben:

1.client> select queue_end('message_q');
+------------------------+
| queue_end('message_q') |
+------------------------+
|                      1 |
+------------------------+
1 row in set (0.00 sec)

Im zweiten Client reservieren wir nun die nächste Nachricht:

2.client> select * from message_q where queue_wait('message_q');
+-----+--------+--------------------+
| num | poster | message            |
+-----+--------+--------------------+
|   2 | papa   | pflanze einen baum |
+-----+--------+--------------------+
1 row in set (0.00 sec)

Es ist - wie zu erwarten war die zweite Nachricht.  Im ersten Client holen wir nun wieder eine Nachricht. Es ist die dritte Nachricht. Zu beachten ist, dass der zweite Client (oben) die Nachricht noch nicht endgültig abgeholt hat.

1.client> select * from message_q where queue_wait('message_q');
+-----+--------+---------+
| num | poster | message |
+-----+--------+---------+
|   3 | chef   | %&#%$   |
+-----+--------+---------+
1 row in set (0.00 sec)

Der zweite Client gibt die Reservierung auf und holt die Nachricht nicht ab.

2.client> select queue_abort('message_q');
+--------------------------+
| queue_abort('message_q') |
+--------------------------+
|                        1 |
+--------------------------+
1 row in set (0.00 sec)

Woraufhin der erste Client mit einem queue_wait() die eben abgeholte Nachricht drei als abgeholt meldet und sich die nun wieder freie zweite Nachricht reserviert.

1.client> select * from message_q where queue_wait('message_q');
+-----+--------+--------------------+
| num | poster | message            |
+-----+--------+--------------------+
|   2 | papa   | pflanze einen baum |
+-----+--------+--------------------+
1 row in set (0.00 sec)

Sollte die Queue leer sein, blockiert/wartet queue_wait() bis eine Eingabe kommt. Der Default liebt bei 60 Sekunden, kann aber selbst definiert werden.

1.client> select * from message_q where queue_wait('message_q',3);
Empty set (2.62 sec)

Q4M beherrscht auch Vergleichsoperatoren (für nicht Strings/Blobs).

1.client> select * from message_q where queue_wait('message_q:num=3');
+-----+--------+---------+
| num | poster | message |
+-----+--------+---------+
|   3 | chef   | %&#%$   |
+-----+--------+---------+
1 row in set (0.00 sec)

Mit SHOW ENGINE QUEUE STATUS ist ein Überblick über die Statusvariablen der Queue-Engine zu erhalten.

mysql> show engine queue status\G
*************************** 1. row ***************************
  Type: QUEUE
  Name:
Status:
I/O calls
------------------------------------
sys_read                            1
sys_write                          14
sys_sync                           14
read_cachehit                       0

Writer thread
------------------------------------
append                              3
remove                             10

Conditional subscription
------------------------------------
evaluation                          5
compile                             1
compile_cachehit                    0

High-level stats
------------------------------------
rows_written                       12
rows_removed                       10
queue_wait                         12
queue_end                           3
queue_abort                         1
queue_rowid                         0
queue_set_srcid                     0

Bei Q4M handelt es sich um eine nette spezialisierte Engine. Zwar wurde hier nicht die aktuellste Version vorgestellt, doch zum Spielen reicht es allemal. Mit q4m-forward  bietet Q4M zudem Nachrichten von einem MySQLd zum nächsten Server zu übermitteln. Einiges ist noch im Tutorial nachzulesen. Viel Spaß:)