maxscaleにはbinlogrouterなる機能がある。

なお、maxscaleのバージョンは2.2、mariadbのバージョンは10.3で確認しています。

これは以下のようなイメージで、バイナリログを蓄積し、他slaveへ伝播させる中継機能を持つ。

----------
mariadb master
IP:192.168.35.11
----------
|
| (replication)
|
----------
maxscale binlogrouter
IP:192.168.35.12
----------
|
| (replication)
|
----------
mariadb slave
IP:192.168.35.13
----------

 

これはmysqlbinlogのremote機能にも似ているが、

通常のレプリケーションと同じように扱える利点があり、

さらにCDC(Change Data Capture)という機能を利用することで、

Kafkaなんかと連携することも可能らしい。

 

面白いアプローチだなと思いつつ、使いどころが難しい。。。

とりあえず、構築手順だけ残しておく。

 

・master(192.168.35.11)

$ sudo mysql_secure_installation
$ sudo mysql -u root -p
[MariaDB]> CREATE USER 'admin' IDENTIFIED BY 'admin1234';
[MariaDB]> GRANT ALL PRIVILEGES ON *.* TO 'admin';

 

・binlogrouter(192.168.35.12)

$ sudo mkdir /var/lib/maxscale/binlog
$ chown maxscale:maxscale /var/lib/maxscale/binlog

 

/etc/maxscale.cnf

[Replication]
type=service
router=binlogrouter
user=admin
passwd=admin1234
server_id=99
binlogdir=/var/lib/maxscale/binlog
mariadb10-compatibility=1
mariadb10_master_gtid=1

[Replication Listener]
type=listener
service=Replication
protocol=MariaDBClient
port=13306
$ mysql -h 127.0.0.1 -P 13306 -u admin -padmin1234
[MariaDB]> CHANGE MASTER TO MASTER_HOST='192.168.35.11', MASTER_PORT=3306, MASTER_USER='admin', MASTER_PASSWORD='admin1234', master_use_gtid=slave_pos;
[MariaDB]> start slave;

※slave_posは最初は空なので、場合によっては、「SET GLOBAL gtid_slave_pos=’XXX’;」が必要。

 

ここまでで、masterとbinlogrouterのレプリケーション関係が構築された。

binlogrouterはバイナリログしか持っていないので、slave_posは「欲しい状態」を指定すればよい。

 

・slave(192.168.35.13)

$ mysqldump -h 192.168.35.11 -u admin -padmin1234 --master-data=2 --single-transaction --routines --all-databases > master.sql
$ mysql -u admin -padmin2612 < master.sql
$ mysql -u admin -padmin2612
[MariaDB]> SET GLOBAL gtid_slave_pos='{master.sqlファイルに書いてあるGTIDポジション}';
[MariaDB]> CHANGE MASTER TO MASTER_HOST='192.168.35.12', MASTER_PORT=13306, MASTER_USER='admin', MASTER_PASSWORD='admin1234', master_use_gtid=slave_pos;
[MariaDB]> start slave;

 

という手順にて、binlogrouterを介したレプリケーションができる。

あとは、有効な使い所を探すべし。。

 

以上

投稿日時:2018年07月25日 12:36   カテゴリー:mariadb  

MariaDBでMultisource Replicationをやるときは、

  • GTIDベース
  • 通常ベース(ファイル/ポジション)

によって、制約が異なる。

 

GTIDベースでは、

レプリケーション元のgtid_domain_idが異なっていなければならない。

通常ベースでは、

レプリケーション元のgtid_domain_idが同じでも良い。

 

つまり、GTIDベースでは、以下のようなケースにおいては、

db01とdb02のgtid_domain_idは異る必要がある。

+++++          +++++
db01           db02
+++++          +++++
|              |
|              |
++++++++++++++++++++
multisource
++++++++++++++++++++

 

しかし、通常ベースでは、gtid_domain_idが同じでもよい。

これは、gtid_domain_idを指定できないAWSのRDS環境のおいても、

Multisource Replicationが使えることを意味している。

つまり、db01とdb02がRDSで、multisourceがEC2という位置付けになる。

※RDS上にmultisourceは組めません。

 

さらに、MariaDBの嬉しい機能として、

コネクションごとに、replicate_do_dbやらreplicate_igonore_dbが指定できる。

たとえば、db01へのコネクション名を「con-db01」、db02へのコネクション名を「con-db02」とすると、

以下のような設定をmultisource側のmy.cnfに記載できる。

con-db01.replicate_do_db=hoge01
con-db02.replicate_do_db=hoge02

この設定の意味するところは、「con-db01」ではhoge01データベースをレプリケーションの対象とする、

「con-db02」ではhoge02データベースをレプリケーションの対象とする、ということである。

 

このような機能を使うことで、

分割されたDBからデータをかき集め、

様々な集計/分析を行うことが容易となる。

さらに、MariaDB10.2からはwindow関数も導入されているため、

Multisource Replicationによる恩恵もおおきくなるだろう。

※MariaDB10.2と10.3では使えるwindow関数が異なるので注意。

 

なお、GTIDを使う場合、以下のように、2つのGTIDを指定する。

gtid_domain_idが10はdb01、20はdb02を指している。

> stop all slaves;
> SET GLOBAL gtid_slave_pos='10-11-10,20-21-10';
> CHANGE MASTER 'con-db01' TO  MASTER_HOST='XXX', MASTER_USER='rpl', MASTER_PASSWORD='rplpassword', master_use_gtid=slave_pos;
> CHANGE MASTER 'con-db02' TO  MASTER_HOST='YYY', MASTER_USER='rpl', MASTER_PASSWORD='rplpassword', master_use_gtid=slave_pos;
> start all slaves;

 

以上

投稿日時:2018年06月14日 23:51   カテゴリー:mariadb  

表題のとおり、MariaDB10.3とOracleをconnectエンジンで繋いでみた。

つなぎ方は、odbcかjdbcを使うことで実現できる。

Oracleのクライアント設定とかも必要なので、

ここでは詳細な設定について割愛。

 

なお、Oracleとの互換性を最大限たかめるため、

SQL_MODE=ORACLE

という、MariaDB10.3で導入されたモードを設定している。

 

おおよそのデータについては、問題なくconnectエンジン経由で扱えるのだが、

最大の難関は日付けである。

Oracleはtimestamp型を拡張しているため、

MariaDB(MySQL)のtimestamp型とカバー範囲が異なる。

そのため、以下の条件のいずれかの場合において、

connectで持ってきた時に「0000-00-00 00:00:00」という日時になってしまう。

  • Oracleのtimestamp型データがMySQLのtimestamp型の範囲外のとき
  • Oracleのtimestamp型データがNULLのとき

前者はその通りの話ではあるが、後者のパターンも有り得るのがやっかいである。

しかも、条件句で指定した時は、NULLと認識するくせに、結果セットが変るのである。

これはconnectで自動作成させるテーブル定義上仕方ないことなのであるが。。

 

その他、BLOB、CLOBなどのバイナリ型が、

connect上はvarcharになるので、このあたりも場合によっては、注意が必要。

 

以上

投稿日時:2018年06月14日 23:16   カテゴリー:mariadb  

MySQL8のJSON部分更新を確認してみた。

 

最初こんなテーブルを作り、データを更新する。

> create database hoge;
> use hoge;
> create table fuga (id integer not null, body json not null, primary key(id));
> insert into fuga values (1, '{"x":10, "y":20}');
> update fuga set body = json_replace(body, '$.y', 50);

この状態でバイナリログを見ると、以下のようになっている。

### UPDATE `hoge`.`fuga`
### WHERE
### @1=1
### @2='{"x": 10, "y": 20}'
### SET
### @1=1
### @2='{"x": 10, "y": 50}'

部分更新じゃなくね?と思ったが、

my.cnfに以下を記載する。

binlog_row_image=minimal
binlog_row_value_options=partial_json

再起動完了後、以下のようなSQLを実行する。

> update fuga set body = json_replace(body, '$.y', 12);

この時、バイナリログは、

### UPDATE `hoge`.`fuga`
### WHERE
### @1=1
### SET
### @2=JSON_REPLACE(@2, '$.y', 12)

となる。

もともと、binlog_row_imageはバイナリログに変更分しか記載しないもので、
5.6から導入されていたが、8ではbinlog_row_value_optionsというパラメータが追加された。
これにより、JSONの部分更新がバイナリログ上でも明示される。

とはいえ、JSONの部分更新を成立させるには、
以下のJSON関数を使う必要がある。

  • JSON_SET()
  • JSON_REPLACE()
  • JSON_REMOVE()

MariaDB10.3でも、binlog_row_value_optionsがないため、

JSONの部分更新はできない。

おまけに、JSONはlongtextのエイリアスのため、

「JSONの正しさ」は保証しない。

JSONを使うなら、virtual columnもあるMSQL8が現時点では良いと思う。

 

(2018年11月1日追記)

> おまけに、JSONはlongtextのエイリアスのため、

> 「JSONの正しさ」は保証しない。

大変申し訳ありません。

上記は間違っていました。

https://mariadb.com/kb/en/library/json-data-type/

にcheck制約を使うことで、「JSONの正しさ」を保証することが可能でした。

 

以上

投稿日時:2018年06月14日 22:46   カテゴリー:mysql  

10.3のパラメータ(innodb関連中心)を確認してみたところ、

10.2でなされたような大きな変更は少なく、

10.2でdepricatedになったものが、removedになったりしている感じであった。

 

10.3の目玉としては、

やはりPL/SQLへの対応なのであろうが、

個人的には、以下が嬉しい。

  • sequenceの導入 → nextvalができる
  • Instant ADD COLUMN → alter tableの時間抑制に繋がりそう

 

その他、spider enginにおいて、条件句のpush downも入ったようだ。

 

以上を踏まえると、10.3への移行する場合、

既に10.2を使っているのであれば、比較的低リスクで移行できる?

と感じています。

 

10.4の計画も見てみると、

innodb以外のストレージエンジンの機能拡張が候補になっていたりと、

MySQLとの乖離がどんどん大きくなるような気がするが。。

 

以上

投稿日時:2018年05月29日 00:10   カテゴリー:mariadb  

先月のMySQL8のGAにつづき、

MariaDB10.3がGAされました。

https://mariadb.com/kb/en/library/changes-improvements-in-mariadb-103/

 

MySQL5.7もそんなにいじってないうちに8が出たので、

これから8を検証しようとしているうちに、10.3が出たのですが、

10.3では気になっていたJSONの改善はなさそうです。

 

そもそもMySQLでは5.7で導入されたJSONがNativeJSONのため、

登録時のvalidationが効くという特性がありました。

MariaDB10.2で導入されたJSONはText型のaliasのようで、

validationは効かないような感じです。(未検証です。すいません。)

ただ、MariaDBのドキュメント読む限り、Textのパースは高速と記載されています。

 

MySQL8ではJSONの部分更新機能が導入されました。

これにより、巨大なJSONの一部を更新しても、更新負荷を下げることが可能となり、

もちろん、バイナリログにもそのように記載されるとのことです。

このようなJSONの部分更新がMariaDB10.3でも入るかと期待してましたが、

そのような記載が見あたらず。。

そもそもdynamic columnsに部分更新があるのかどうかにさえ、今気付いた。。

 

というわけで、

  • MySQL8
  • MariaDB10.3

の2つのGAについて、まずはJSONまわりから挙動確認していこうと考えてます。

 

以上

投稿日時:2018年05月26日 23:47   カテゴリー:mariadb, mysql  

centos7でstart-stop-daemonが使いたいなと思い、

調べていると、epelレポジトリにdpkgというパッケージがあった。

https://apps.fedoraproject.org/packages/dpkg/

 

ってわけで、


# yum install dpkg --enablerepo=epel

で、start-stop-daemonコマンド入りのdpkgがインストール可能である。

ソースからビルドする必要あると思っていたのに。。

 

以上

投稿日時:2018年04月21日 23:37   カテゴリー:centos, fedora  

Google Computer Cloud(以下、GCP)で、

グローバルロードバランサの背後にnginxを置く場合は、

keepaliveをしておこう。

 

(参考)

https://blog.percy.io/tuning-nginx-behind-google-cloud-platform-http-s-load-balancer-305982ddb340

 

これやっておかないと、結構502がでる。

ちなみに、650秒をkeepalive_timeoutにするなら、

ロードバランサのタイムアウトは600秒程度、つまり少し短めがよいとのこと。

 

実は、AWSのELBも同様。

 

とはいえ、結構な高負荷にならないと、本事象は目立ってこないので、

なかなか難しいところである。

 

以上

投稿日時:2018年04月09日 23:07   カテゴリー:gcp, nginx  

std::unorderd_mapになんでも入れてみた。

とりあえずコード。

 

class Session
{
private:
    std::unordered_map<std::string, std::shared_ptr<void>> values;

public:
    template <typename T>
    bool getValue(const std::string &name, T *value)
    {
        std::shared_ptr<void> data = values[name];
        if (data != nullptr) {
            void* raw = data.get();
            *value = *((T*) raw);
            return true;
        }
        return false;
    }

    template <typename T>
    void setValue(const std::string &name, T *value)
    {
        values.emplace(std::make_pair(name, std::make_shared<T>(*value)));
    }
};

struct STest
{
    int m1;
    std::string m2;
};

int main()
{
    STest t1{2, "hoge"};
    Session s;
    s.setValue<STest>("key", &t1);

    STest t2;
    if (s.getValue<STest>("key", &t2)) {
        std::cout << "m1:" << t2.m1 << ", m2:" << t2.m2 << std::endl; // 2, hoge
    }
}

 

となる。

コンテナから引き出す時に、無駄に1回生成が走るから、

大きいインスタンスでは注意が必要。

 

で、なぜこれをつくるかというと、

例えばTCPの常時接続の際、

セッションで引き回せると、

簡単に値を保持できるという狙いのため。

 

とはいえ、ポインタのキャスト多いから、大丈夫かはわからん。。

確実な使い方しないと危なそう。。

 

以上

投稿日時:2018年04月09日 22:49   カテゴリー:c/c++  

mysqlサーバが突然死して、

クライアント側から見ると生きている状態、つまりハーフCLOSE状態となるのだが、

このとき、他のmysqlサーバに接続している状態で、上記が発生すると、

生きている側のmysqlサーバへの接続がCLOSEされない待ち状態となることがある。

HTTPアクセスからだと、以下のような場合が該当する。

  1. mysqlサーバAにconnect
  2. mysqlサーバBにconnect
  3. mysqlサーバAにbegin
  4. mysqlサーバBにbegin
  5. mysqlサーバAにselect for update
  6. mysqlサーバBにselect
  7. mysqlサーバBが突然死
  8. HTTP要求がタイムアウト

このとき、6の応答を待ったまま、処理が続行され、

5のロックが解除されない状態となる。

5のロックを解除するには、

  • mysqlサーバAからプロセスをkillする
  • HTTPサーバを再起動する
  • 実行タイムアウトまで待つ

という方法のいずれかになる。

JDBCではsocketTimeoutといういわゆる実行タイムアウトがあるのだが、

phpのPDOでは、デフォルトで実行タイムアウトの概念はない。

set session 〜 で対応するくらいになるだろう。

しかし、set session 〜 で出来ることは、mysqlサーバのクエリー実行時間の制御であり、

クライアント側のタイムアウト制御はできない。

そこで、あのmysqlndを使っているのであれば、

mysqlnd.net_read_timeout

というパラメータを設定することで、実行タイムアウト例外を検知できるようになる。

apacheであれば、virtualhostに、php-fpmであれば、www.confに設定できる。

 

このように実行タイムアウト、いわゆるexecution timeoutをちゃんと処理していないことで、

痛い目を見る可能性があるので、このあたりはチェックしておくといざというときに大きな障害を回避できる。

 

以上

投稿日時:2018年03月15日 23:33   カテゴリー:mysql, php