GA

ラベル Linux の投稿を表示しています。 すべての投稿を表示
ラベル Linux の投稿を表示しています。 すべての投稿を表示

2025/06/17

バックアップをネットワークまたいだファイルサーバに置きたいけど指定した以外のファイルは上書きもされたくないし読まれたくもない

TL;DR


普段から使っていたのはファイルサーバー側でポートを開いてリダイレクトしちゃう。実際にはもう少し「ランダムにポートを開いてその番号を教えてくれるスクリプトだけをssh越しに実行させる」とか「curlで叩くとトークン検証してランダムにポートを開いてその番号を教えてくれるAppにする」とかの工夫はできる。

nc -l 13306 > /path/to/myfile

バックアップはリモートから書き込む。bash使いなので /dev/tcp は好き。

xtrabackup -S /usr/mysql/8.4.5/data/mysql.sock -uroot --backup --stream=xbstream | pv >  /dev/tcp/file_server/13306

書き込みが終わる(異常終了を含む)とncプロセスは落ちるので再利用のためにはレシーバー側でncの再実行が必要。そのタイミングでリダイレクト先のファイル名を変更できるので上書きも回避できる(noclobberでエラーにするか、別のファイルとして受けるかの違いではある)

もし通信経路を通す前に圧縮したいなら送信元で圧縮すればいいし、

xtrabackup -S /usr/mysql/8.4.5/data/mysql.sock -uroot --backup --stream=xbstream | pzstd -c | pv  > /dev/tcp/file_server/13306

受信側でそのまま展開したいならncからパイプしてやればいい(xbstreamまではパイプで繋げるけどxtrabackup —prepareは別でやらないといけない)

nc -l 13306 | pzstd -dc | xbstream -x

受ける側でパスワードをかけたりもした(ランダムに推測不可能なものを作ってDBAにだけメールしてた気がする)

nc -l 13306 | openssl aes-256-cbc -pbkdf2 -e -pass pass:something > /tmp/myfile.enc

$ file /tmp/myfile.enc
/tmp/myfile.enc: openssl enc'd data with salted password

$ openssl aes-256-cbc -pbkdf2 -d -in /tmp/myfile.enc -out /tmp/myfile
enter AES-256-CBC decryption password:

$ pzstd -dc /tmp/myfile | xbstream -x
/tmp/myfile         : 75159378 bytes

デーモンにしたりすると面倒だけど後々が楽。。

2024/09/02

MySQLだけでファイルシステムフルやMySQLだけでファイルシステムハングを再現するアイディア

TL;DR

  • 障害時の動作を確認するためにファイルシステムをフルにしたり xfs_freeze をかけたいことはままある
  • が、MySQLの動作パーティションが分かれていない場合、本当にやると調査用のシェルまで動けなくなってしまう
  • ループバックデバイスを使ってdatadirだけ起こすのが楽

↑だけで全部言い尽くしてしまった。
取り敢えずdatadirに割り当てる好きなサイズのファイルを切り出す。

[yoku0825@yoku0825-conoha work]$ dd if=/dev/zero of=./datadir bs=1M count=300
300+0 records in
300+0 records out
314572800 bytes (315 MB) copied, 0.26401 s, 1.2 GB/s

[yoku0825@yoku0825-conoha work]$ ll -h
total 301M
-rw-r--r-- 1 yoku0825 yoku0825 300M Sep  2 13:20 datadir

ファイルシステムを作ってから好きなマウントディレクトリに割り当てる。

[yoku0825@yoku0825-conoha work]$ mkfs -t xfs ./datadir
meta-data=./datadir              isize=512    agcount=4, agsize=19200 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=0, sparse=0
data     =                       bsize=4096   blocks=76800, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
log      =internal log           bsize=4096   blocks=855, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

[yoku0825@yoku0825-conoha work]$ sudo mount ./datadir /mnt

[yoku0825@yoku0825-conoha work]$ df -h /mnt
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop1      297M   16M  282M   6% /mnt

既存のdatadirの中身を /mnt に移してシンボリックリンクにするなり、 --datadir=/mnt--initialize するなり、 ラジバンダリ
俺は yoku0825 というOSアカウントで mysqld を起動するので、オーナーを変えてしまっている。

[yoku0825@yoku0825-conoha work]$ sudo chown -R yoku0825. /mnt

[yoku0825@yoku0825-conoha work]$ /usr/mysql/8.4.2/bin/mysqld --no-defaults --datadir=/mnt --initialize-insecure
2024-09-02T04:21:51.223351Z 0 [System] [MY-015017] [Server] MySQL Server Initialization - start.
2024-09-02T04:21:51.223497Z 0 [Warning] [MY-010139] [Server] Changed limits: max_open_files: 1024 (requested 8161)
2024-09-02T04:21:51.223507Z 0 [Warning] [MY-010142] [Server] Changed limits: table_open_cache: 431 (requested 4000)
2024-09-02T04:21:51.225861Z 0 [System] [MY-013169] [Server] /usr/mysql/8.4.2/bin/mysqld (mysqld 8.4.2) initializing of server in progress as process 22201
2024-09-02T04:21:51.235592Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2024-09-02T04:21:51.565478Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
2024-09-02T04:21:52.610123Z 5 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option.
2024-09-02T04:21:55.333522Z 0 [System] [MY-015018] [Server] MySQL Server Initialization - end.

[yoku0825@yoku0825-conoha work]$ /usr/mysql/8.4.2/bin/mysqld --no-defaults --datadir=/mnt --daemonize          
mysqld will log errors to /mnt/yoku0825-conoha.err
mysqld is running as pid 22300

テキトーにbinlogを吐きそうな何かをしてやると

[yoku0825@yoku0825-conoha work]$ mysqlslap -S /tmp/mysql.sock -uroot --auto-generate-sql --auto-generate-sql-execute-number=10000000 --auto-generate-sql-load-type=w -c 10

[yoku0825@yoku0825-conoha work]$ df -h /mnt
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop1      297M  297M   20K 100% /mnt

[yoku0825@yoku0825-conoha work]$ tail /mnt/yoku0825-conoha.err 
2024-09-02T04:22:12.892895Z 0 [Warning] [MY-010139] [Server] Changed limits: max_open_files: 1024 (requested 8161)
2024-09-02T04:22:12.892905Z 0 [Warning] [MY-010142] [Server] Changed limits: table_open_cache: 431 (requested 4000)
2024-09-02T04:22:13.150060Z 0 [System] [MY-010116] [Server] /usr/mysql/8.4.2/bin/mysqld (mysqld 8.4.2) starting as process 22298
2024-09-02T04:22:13.177164Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2024-09-02T04:22:13.436646Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
2024-09-02T04:22:13.796358Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2024-09-02T04:22:13.796400Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
2024-09-02T04:22:13.821037Z 0 [System] [MY-010931] [Server] /usr/mysql/8.4.2/bin/mysqld: ready for connections. Version: '8.4.2'  socket: '/tmp/mysql.sock'  port: 3306  Source distribution.
2024-09-02T04:22:13.821757Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /tmp/mysqlx.sock
2024-09-02T04:26:29.994586Z 27 [ERROR] [MY-000035] [Server] Disk is full writing './binlog.000001' (OS errno 28 - No space left on device). Waiting for someone to free space... Retry in 60 secs. Message reprinted in 600 secs.     <--- これ
[yoku0825@yoku0825-conoha work]$ mysql -S /tmp/mysql.sock -uroot                                             
mysql> SHOW PROCESSLIST;
+----+-----------------+-----------+-----------+---------+------+----------------------------+------------------------------------------------------------------------------------------------------+
| Id | User            | Host      | db        | Command | Time | State                      | Info                                                                                                 |
+----+-----------------+-----------+-----------+---------+------+----------------------------+------------------------------------------------------------------------------------------------------+
|  5 | event_scheduler | localhost | NULL      | Daemon  |  341 | Waiting on empty queue     | NULL                                                                                                 |
| 19 | root            | localhost | mysqlslap | Sleep   |  115 |                            | NULL                                                                                                 |
| 20 | root            | localhost | mysqlslap | Query   |   85 | waiting for handler commit | INSERT INTO t1 VALUES (1665728802,'8qB5MAR1caTvNFXB00HKShGa1uToGCMP2ZMHjpABFg6fnTioTz8pZGNQAkEJwckr9 |
| 21 | root            | localhost | mysqlslap | Query   |   85 | waiting for handler commit | INSERT INTO t1 VALUES (866596855,'naQuzhMt1IrZIJMkbLAKBNNKKK2sCknzI5uHeGAgQuDd5SLgpN0smODyc7qorTo1Qa |
| 22 | root            | localhost | mysqlslap | Query   |   85 | waiting for handler commit | INSERT INTO t1 VALUES (100669,'qnMdipW5KkXdTjGCh2PNzLoeR0527frpQDQ8uw67Ydk1K06uuNHtkxYBxT5w8plb2Bbpz |
| 23 | root            | localhost | mysqlslap | Query   |   85 | waiting for handler commit | INSERT INTO t1 VALUES (1137990260,'pvYr3YntZ2DoGrwWfL91bW9Epw8iO6vDuR4xrkqOe3Dum1PPEQQpwRvmO3Kg2Ftob |
| 24 | root            | localhost | mysqlslap | Query   |   85 | waiting for handler commit | INSERT INTO t1 VALUES (2010720737,'iLEkea9eOuLJweKJjQ3IJlW1dXTSs6dbJjJpHTRZFgkBJ4XuNQ4iCRchy51r3WQEE |
| 25 | root            | localhost | mysqlslap | Query   |   85 | waiting for handler commit | INSERT INTO t1 VALUES (866596855,'naQuzhMt1IrZIJMkbLAKBNNKKK2sCknzI5uHeGAgQuDd5SLgpN0smODyc7qorTo1Qa |
| 26 | root            | localhost | mysqlslap | Query   |   85 | waiting for handler commit | INSERT INTO t1 VALUES (100669,'qnMdipW5KkXdTjGCh2PNzLoeR0527frpQDQ8uw67Ydk1K06uuNHtkxYBxT5w8plb2Bbpz |
| 27 | root            | localhost | mysqlslap | Query   |   85 | waiting for handler commit | INSERT INTO t1 VALUES (1759592334,'3lkoxjtvgLu5xKHSTTtJuGE5F5QqmCcppCTmvFZScRZQgim93gSxwb24gKmIPEzEQ |
| 28 | root            | localhost | mysqlslap | Query   |   85 | waiting for handler commit | INSERT INTO t1 VALUES (866596855,'naQuzhMt1IrZIJMkbLAKBNNKKK2sCknzI5uHeGAgQuDd5SLgpN0smODyc7qorTo1Qa |
| 29 | root            | localhost | mysqlslap | Query   |   85 | waiting for handler commit | INSERT INTO t1 VALUES (1759592334,'3lkoxjtvgLu5xKHSTTtJuGE5F5QqmCcppCTmvFZScRZQgim93gSxwb24gKmIPEzEQ |
| 30 | root            | localhost | NULL      | Query   |    0 | init                       | SHOW PROCESSLIST                                                                                     |
+----+-----------------+-----------+-----------+---------+------+----------------------------+------------------------------------------------------------------------------------------------------+
13 rows in set, 1 warning (0.01 sec)

とまあファイルシステムが埋まるとbinlogが吐けなくて waiting for handler commit で突き刺さることがわかります。

mysql コマンドラインクライアントでの新規接続には成功するけど SHOW BINARY LOGS が刺さったり (binlogを参照するためのmutexが競合する)、 performance_schema.processlist は読み取れる (MySQL 8.4の SHOW PROCESSLIST はこれを読む)けど information_schema.processlist は読めない (テンポラリーテーブルを作るので失敗した)とか

mysql> SELECT * FROM information_schema.processlist;
ERROR 1114 (HY000): The table '/tmp/#sql571c_22_0' is full

mysql> SELECT * FROM performance_schema.processlist;                                                                       
..
14 rows in set (0.04 sec)

SHUTDOWN ステートメントは刺さるかなと思ったら刺さらずにちゃんと停止したり、 binlog_error_action はファイルシステムフルではトリガーされないのだなあと思ったり、学びはあると思います

2017/12/19

MySQLジャンキーにngx_mrubyを与えた結果

この記事は mod_mruby ngx_mruby Advent Calendar 2017 の18日目の記事です。
時間オーバーしてますごめんなさい。
本題に入る前に、この記事をご覧の方の中に “MySQL HTTP Plugin” をご存知の方はいらっしゃるでしょうか? (MySQL Casualな方々はこの辺でオチの予想がついたはず)
MySQL HTTP Pluginは2014年ころに MySQL Labs で公開されていた「実験室版」として配布されていて、 MySQLがHTTPをしゃべるようになるプラグイン です。
何言ってるのかよくわからないとか、誰得? とか思うかも知れませんが、そんな細かいことを気にしてはいけません。とにかく、MySQL(mysqld)がHTTPをしゃべったんです。
かつての面影(?)はこのあたりの記事とか資料に見て取ることができます。
MySQL Labsは「α前のステータスのプロダクトを取り敢えず出してみる場」らしいので(要出典)、 GAになってから1度もメジャーバージョンアップできずに最短ライフタイムサイクルで消えた ナントカ とは違い、まあそういうものだし当時は俺も「誰得wwww」とか言ってたんですが、
俺が遊び始めて「あー面白いかも」と思った途端に消える
という
いつもの流れになって寂しく思っていたところです。
「そこで ngx_mruby ですよ」
誰かの声が聞こえた気がしたので試してみました。 やってること自体は単なるHello, Worldです。

ngx_mrubyのインストールには nginxにngx_mrubyをインストールする - Qiita を大変参考にさせていただきました。 2014年のアドベントカレンダーの記事が3年経ってMySQLジャンキーを救う(?)、インターネッツですね。
あとはペパボの中の人にパッケージをビルドするヤーツも教えてもらいました。最終的にはこれで作ったrpmをインストールしています。便利!
ngx_mruby/build_config.rbconf.gem :github => 'mattn/mruby-mysql' だけ追加しました。
MySQLに接続しないソフトウェアに興味が持てなううんなんでもない。
/etc/nginx/conf.d/default.conf によしなにこんな記述を追加。 /query をエンドポイントにして後ろに渡されたクエリーをそのままローカルのmysqldに受け渡してJSONで戻す(えっ)
    location /query {
      mruby_content_handler_code '
        req = Nginx::Request.new
        sql = req.uri.gsub(/\/query\//, "")

        conn = MySQL::Database.new("127.0.0.1", "root", "", "world", 64057)
        ret= []

        conn.execute(sql) do |row, field|
          one_row= []
          for n in 0..field.count - 1 do
            one_row.push({field[n] => row[n]})
          end
          ret.push(one_row)
        end

        req.content_type = "application/json"
        Nginx.rputs JSON::stringify(ret)
      ';
    }
こんな感じ。
たったこれだけのものを書くのに結構長い時間がかかった。プログラム力の衰えを感じる。。
で、出来上がったMySQL HTTP Pluginごっこはこんな感じ。
$ http localhost/query/SELECT%20%2A%20FROM%20city%20LIMIT%203
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 287
Content-Type: application/json
Date: Tue, 19 Dec 2017 06:24:38 GMT
Server: nginx/1.13.7

[
    [
        {
            "ID": 1
        },
        {
            "Name": "Kabul"
        },
        {
            "CountryCode": "AFG"
        },
        {
            "District": "Kabol"
        },
        {
            "Population": 1780000
        }
    ],
    [
        {
            "ID": 2
        },
        {
            "Name": "Qandahar"
        },
        {
            "CountryCode": "AFG"
        },
        {
            "District": "Qandahar"
        },
        {
            "Population": 237500
        }
    ],
    [
        {
            "ID": 3
        },
        {
            "Name": "Herat"
        },
        {
            "CountryCode": "AFG"
        },
        {
            "District": "Herat"
        },
        {
            "Population": 186800
        }
    ]
]
おおなんかMySQL HTTP Pluginっぽい! ちなみに本家MySQL HTTP Pluginの出力結果はこんな感じでした。結構違う。
ってか実データとメタデータが分離されてた。それでよかったのか。。
$ http http://a:b@localhost:8080/sql/world/SELECT+%2A+FROM+city+LIMIT+3
HTTP/1.1 200 OK
Cache-control: must-revalidate
Connection: Keep-Alive
Content-Length: 1078
Content-Type: application/json
Pragma: no-cache
Server: MyHTTP 1.0.0-alpha

[
    {
        "data": [
            [
                "1",
                "Kabul",
                "AFG",
                "Kabol",
                "1780000"
            ],
            [
                "2",
                "Qandahar",
                "AFG",
                "Qandahar",
                "237500"
            ],
            [
                "3",
                "Herat",
                "AFG",
                "Herat",
                "186800"
            ]
        ],
        "meta": [
            {
                "catalog": "def",
                "charset": 63,
                "column": "ID",
                "database": "world",
                "decimals": 0,
                "flags": 16899,
                "length": 11,
                "org_column": "ID",
                "org_table": "city",
                "table": "city",
                "type": 3
            },
            {
                "catalog": "def",
                "charset": 33,
                "column": "Name",
                "database": "world",
                "decimals": 0,
                "flags": 1,
                "length": 105,
                "org_column": "Name",
                "org_table": "city",
                "table": "city",
                "type": 254
            },
            {
                "catalog": "def",
                "charset": 33,
                "column": "CountryCode",
                "database": "world",
                "decimals": 0,
                "flags": 16393,
                "length": 9,
                "org_column": "CountryCode",
                "org_table": "city",
                "table": "city",
                "type": 254
            },
            {
                "catalog": "def",
                "charset": 33,
                "column": "District",
                "database": "world",
                "decimals": 0,
                "flags": 1,
                "length": 60,
                "org_column": "District",
                "org_table": "city",
                "table": "city",
                "type": 254
            },
            {
                "catalog": "def",
                "charset": 63,
                "column": "Population",
                "database": "world",
                "decimals": 0,
                "flags": 1,
                "length": 11,
                "org_column": "Population",
                "org_table": "city",
                "table": "city",
                "type": 3
            }
        ],
        "status": [
            {
                "server_status": 34,
                "warning_count": 0
            }
        ]
    }
]



まあなんかよしなに楽しんでみました。実際のところ(MySQL的な文脈では)何に使えるだろう。。

2017/12/18

InnoDB: Fatal : Cannot initialize AIO sub-system でmysqldが起動しない…だと…

1サーバーにゴツゴツmysqldを詰め込んでいる環境で、ふとこんなエラーでmysqldが起動しなかった。
2017-12-18 17:50:38 12256 [Note] InnoDB: Using Linux native AIO
2017-12-18 17:50:38 12256 [Note] InnoDB: Using CPU crc32 instructions
2017-12-18 17:50:38 7f2b86333740 InnoDB: Warning: io_setup() failed with EAGAIN. Will make 5 attempts before giving up.
InnoDB: Warning: io_setup() attempt 1 failed.
InnoDB: Warning: io_setup() attempt 2 failed.
InnoDB: Warning: io_setup() attempt 3 failed.
InnoDB: Warning: io_setup() attempt 4 failed.
InnoDB: Warning: io_setup() attempt 5 failed.
2017-12-18 17:50:40 7f2b86333740 InnoDB: Error: io_setup() failed with EAGAIN after 5 attempts.
InnoDB: You can disable Linux Native AIO by setting innodb_use_native_aio = 0 in my.cnf
2017-12-18 17:50:40 12256 [ERROR] InnoDB: Fatal : Cannot initialize AIO sub-system
2017-12-18 17:50:40 12256 [ERROR] Plugin 'InnoDB' init function returned error.
2017-12-18 17:50:40 12256 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
2017-12-18 17:50:40 12256 [ERROR] Unknown/unsupported storage engine: InnoDB
2017-12-18 17:50:40 12256 [ERROR] Aborting

2017-12-18 17:50:40 12256 [Note] Binlog end
2017-12-18 17:50:40 12256 [Note] /usr/local/mysql5626/bin/mysqld: Shutdown complete
珍しい。
AIO関連のエラーなんてMySQL 5.5がGAになってしばらくの時代に、 変なファイルシステム(なんだか忘れた)の上にtmpdirを作ろうとして「そのファイルシステムはAIOに対応していない!」と怒られて以来な気がする。
そういえば最近見ないなアレ。CentOS 5.xがいかんかったのか。
取り敢えずファイルシステムを見てみるに、
# grep vg_00-lv_root /proc/mounts
/dev/mapper/vg_00-lv_root / xfs rw,relatime,attr2,inode64,logbsize=256k,sunit=512,swidth=3584,noquota 0 0
xfsであってAIO非対応な変なファイルシステムではない。 というか他のmysqldは元気でゴリゴリ動いているので、この1プロセスだけAIOがどうこう言われるのがそもそもおかしい。
ググると innodb_use_native_aio = 0 にすればいいよ! とか結構出てくる(というかエラーログもそういってる)けれど、Linux AIOが使えないはずはないんだからそれじゃ気持ちが悪い。
結局、ぐるぐる回って @yoheiaさん のところにたどり着いた。
libaio の aio コンテキスト数を調べる - ablog
# cat /proc/sys/fs/aio-max-nr
65536

# cat /proc/sys/fs/aio-nr
63056
うわーい、ぴっちぴち。
というわけで aio-max-nr を増やしたら無事起動するようになりました。
# echo 131072 > /proc/sys/fs/aio-max-nr
# cat /proc/sys/fs/aio-max-nr
131072
ibdファイルかなりオープンされてるのね…。ちょっと意外。
# lsof | grep mysqld | grep -c ibd
75451

2017/10/04

ファイルを吐かないtarコマンドの進捗を確認する

ファイルを吐かないというのは、MySQLのdatadirをtarボールに固めながら圧縮してS3にアップロードするようなケース(なんて限定的)
$ tar -C /var/lib -c mysql | pzstd -qc | aws s3 cp - s3://...
S3さん、アップロード終わるまで状況が見えないので、保管先のファイルサイズから全体をざっくり見積もることができなかった。俺のやり方が悪いだけかも知れない。
取り敢えず lsof で今掴んでいるファイルを見ることくらいまではぱっと思い付いた。
$ ps auxww | grep tar
root      1181  0.0  0.4 289208  4800 ?        Ssl  23:15   0:00 /usr/bin/docker-containerd-current -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --shim docker-containerd-shim --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --runtime docker-runc --runtime-args --systemd-cgroup=true
yoku0825  3259  0.3  0.1 123344  1232 pts/0    S+   23:22   0:00 tar c data
yoku0825  3263  0.0  0.0 112644   968 pts/1    R+   23:22   0:00 grep --color=auto tar

$ lsof -p 3259
COMMAND  PID     USER   FD   TYPE DEVICE  SIZE/OFF      NODE NAME
..
tar     3259 yoku0825    0u   CHR  136,0       0t0         3 /dev/pts/0
tar     3259 yoku0825    1w  FIFO    0,8       0t0     28213 pipe
tar     3259 yoku0825    2u   CHR  136,0       0t0         3 /dev/pts/0
tar     3259 yoku0825    3r   DIR  253,0      4096  71088914 /usr/mysql/5.7.19/data
tar     3259 yoku0825    4r   REG  253,0  21091934  68051378 /usr/mysql/5.7.19/data/bin.000011
たとえば今は bin.000011 を掴んでる、というところまではわかる。
が、 tar コマンドってファイルをどういう順番で掴むのかがわからず、bin.000011 が全体で何番目のファイルなのかよくわからない。 経験則として、 tar cvf とか tar xvf でファイル名だだーっと流している時でも、少なくともファイルパスでソートされているようには見えない。
困った時のソースコード。
ディレクトリーをターゲットにtarボールを作る時には create_archivedump_filedump_file0dump_dirget_directory_entries と来て、その中からgnulibstreamsavedir を呼び出している。
streamsavedir がやってることは単に readdir しているだけで、この時の並び順は特に決まった規則がある訳ではないらしい。
ところで readdir したものをそのままといえば、ls のソート無しオプションである -f な訳で、
$ ls -fl 
total 239212
drwxr-x---  9 yoku0825 yoku0825     4096 Oct  3 23:10 .
drwxrwxr-x 11 yoku0825 yoku0825      148 Jul 18 15:13 ..
-rw-r-----  1 yoku0825 yoku0825 79691776 Oct  3 23:10 ibdata1
-rw-r-----  1 yoku0825 yoku0825       56 Jul 18 15:12 auto.cnf
drwxr-x---  2 yoku0825 yoku0825     4096 Aug 22 18:29 mysql
...
drwxr-x---  2 yoku0825 yoku0825     4096 Aug 10 15:10 i_s
drwxr-x---  2 yoku0825 yoku0825     8192 Aug 10 15:10 p_s
-rw-r-----  1 yoku0825 yoku0825  3818805 Aug 18 18:52 bin.000006
-rw-r-----  1 yoku0825 yoku0825 21091934 Aug 24 18:41 bin.000011
-rw-r-----  1 yoku0825 yoku0825 48221936 Sep  6 12:44 bin.000012
-rw-r-----  1 yoku0825 yoku0825    86553 Sep 15 15:47 bin.000017
-rw-r-----  1 yoku0825 yoku0825    10626 Sep 20 16:30 bin.000024
-rw-r-----  1 yoku0825 yoku0825      169 Aug 22 14:26 relay.000001
-rw-r-----  1 yoku0825 yoku0825      209 Sep 12 13:16 bin.000015
-rw-r-----  1 yoku0825 yoku0825      227 Sep 19 20:49 bin.000022
大体残り50MBくらいかなあと。ディレクトリー階層があるところなら親ディレクトリーがどの場所に並んでて、そのディレクトリーの中でファイルが何番目に位置しているか…とか丁寧に調べれば大体今何%のところにあるかは出せるような気がする。
とはいえまあ pipe viewer 使えばいいよねとは思う。
$ tar -C /var/lib -c mysql | pv | pzstd -qc | aws s3 cp - s3://...
 247MiB 0:00:04 [50.2MiB/s] [         <=>
pv 使うの忘れた時とかcronからキックされた時用かな。

2014/09/16

LOAD DATA INFILEで複数ファイルを食わせるのにちょっとだけ有効なTIPS

LOAD DATA INFILEで複数のファイルを順番(じゃなくてもいいけど)食わせる場合を考える。

↓こんなかんじ


$ ll *.tsv
-rw-rw-r-- 1 mysql mysql 541810051 Sep 16 10:51 20140912.tsv
-rw-rw-r-- 1 mysql mysql 523910384 Sep 16 10:52 20140913.tsv
-rw-rw-r-- 1 mysql mysql 513351897 Sep 16 10:53 20140914.tsv
-rw-rw-r-- 1 mysql mysql 513221906 Sep 16 10:54 20140915.tsv


無圧縮なファイルなら、シェルでぐるんぐるん回すのが便利。

$ for f in *.tsv ; do
> mysql -sse "LOAD DATA INFILE $f INTO TABLE .." && gzip $f
> done


成功したらgzip圧縮とかそのまんま書けるのでラク。
ただし、圧縮ファイルだと"展開" => "LOAD DATA INFILE" => "再圧縮"とか書くのはちょっと面倒だしなんかイヤだ。
あと、InfiniDBはある程度(100万行くらいだっけ?)まとめてLOAD DATA INFILEに食わせた方がデータの構築とかいっぺんに出来て速いとかそんな感じだった気がする(infinidb_use_import_for_batchinsert= 1の場合。0(MySQLのLOAD DATA INFILEをそのまま使う)場合はよく知らない)

という訳で、mkfifoを使ってます。


$ ll *.tsv.gz
-rw-rw-r-- 1 mysql mysql  2778925 Sep 12 18:04 20140722.tsv.gz
-rw-rw-r-- 1 mysql mysql  6775101 Sep 12 18:04 20140723.tsv.gz
-rw-rw-r-- 1 mysql mysql  7715720 Sep 12 18:04 20140724.tsv.gz
-rw-rw-r-- 1 mysql mysql  7811469 Sep 12 18:04 20140725.tsv.gz
-rw-rw-r-- 1 mysql mysql  8329598 Sep 12 18:05 20140726.tsv.gz
-rw-rw-r-- 1 mysql mysql  8256156 Sep 12 18:05 20140727.tsv.gz
-rw-rw-r-- 1 mysql mysql  8081465 Sep 12 18:05 20140728.tsv.gz
-rw-rw-r-- 1 mysql mysql  7881025 Sep 12 18:05 20140729.tsv.gz
-rw-rw-r-- 1 mysql mysql  7496167 Sep 12 18:06 20140730.tsv.gz
-rw-rw-r-- 1 mysql mysql  7934078 Sep 12 18:06 20140731.tsv.gz

$ mkfifo /tmp/fifo

$ zcat *.tsv.gz > /tmp/fifo


別のターミナルから

$ mysql -sse "LOAD DATA INFILE /tmp/fifo INTO TABLE .."

どうせLOAD DATA INFILEに失敗したらTRUNCATEして入れなおすからこれで十分。

2013/05/22

/usr/bin/gcoreのバグ(gdb-7.2-56.el6, gdb-7.2-60.el6で確認)

gdbには/usr/bin/gcoreというシェルスクリプトが入っていて、PIDを引数で渡すと、
そのプロセスにアタッチ -> coreファイル作成 -> デタッチ を流してやってくれるというもの。

PIDを複数渡した場合はそれら全てのプロセスに対してこれをやってくれるはずなんだけど、
ちょっとバグがあって上手く行かない。

..
 62 # Loop through pids
 63 for pid in $*
 64 do
 65         # Write gdb script for pid $pid.
 66         cat >>$tmpfile <<EOF
 67 set pagination off
 68 set width 0
 69 set height 0
 70 attach $pid
 71 gcore $name.$pid
 72 detach
 73 quit
 74 EOF
 75
 76         # `</dev/null' to avoid touching interactive terminal if it is
 77         # available but not accessible (SIGTTIN risk)
 78         gdb -x $tmpfile -batch </dev/null
..


テンポラリファイルにgdbコマンドを詰め込んでgdb -xで非対話に食わせるわけだけれども、
これに2つ以上のPIDを渡すと、

1) 1つめのPIDについてgdbコマンドを$tmpfileに書き込む
2) gdb非対話モードで$tmpfileを実行
 2-1) 表示を整えて
 2-2) 1つめのPIDにアタッチして
 2-3) core吐かせて
 2-4) デタッチして
 2-5) gdbを終了
3) 2つめのPIDについて、gdbコマンドを$tmpfileに追記する
4) gdb非対話モードで$tmpfileを実行
 4-1) 2-1~2-5をやる
 4-2) gdbが既に終了しているので2つ目のPIDについて$tmpfileに追記された部分は読み取られない
5) 3つ目のPIDについて..


Σ(゚д゚lll) ダメじゃん!?

66行目の>>を>に書き換えたらちゃんと動きました。
bug-gdbでも誰も何も言わないというのは、まあ誰も使わないってことだろうか。

https://lists.gnu.org/mailman/listinfo/bug-gdb/


MySQL Bugs、簡単に投稿出来て良いなぁとか思った。
でもMySQLのバグメーリングリストは死んでるよね。
もともとはBugsに上げたらMLにも投げよう、とかそういう感じだった気がするんだけども。

2013/05/15

linux-fincoreを使ってページキャッシュを覗く

ページキャッシュにどれだけページが載っているかを調べたいなーと思うと、
ファイルがページキャッシュに乗っているかどうかを調べる というのがよく引っかかって、
そもそもfincoreってどっから出てきた名前なんだと思ったらlinux-ftoolsというところに行き着いた。

linux-ftoolsはApacheライセンスで、PerlとINLINE Cで書かれたfincoreはGPLv2なんだけど、
どっちが本家なんだかもともとオリジナルがあるのかよく判らない。


取り敢えずlinux-ftoolsを落としてきてコンパイル。
ダウンロード可能なtarballが存在しないので、hgとやらでcloneするしかなさそう。
hgコマンドが入ってなかったので、mercurialパッケージを突っ込んでから(恥ずかしながらこれ知らなかった。。)

$ sudo yum install -y mercurial
$ hg clone https://code.google.com/p/linux-ftools/
$ cd linux-fincore
$ ./configure && make

makeさえすればバイナリ単品で使い回せそうなので、make installはしない前提でconfigureオプションなし。

$ ./linux-fincore /tmp/*

filename                                                                                       size        total_pages    min_cached page       cached_pages        cached_size        cached_perc
--------                                                                                       ----        -----------    ---------------       ------------        -----------        -----------
/tmp/access.log                                                                             191,010                 47                 -1                  0                  0               0.00
/tmp/logs.sql.201305091141.gz                                                                 8,908                  3                 -1                  0                  0               0.00
/tmp/logs.sql.201305131834                                                                   72,382                 18                 -1                  0                  0               0.00
---
total cached size: 0

うむ。-Lつけて縦表示にした方が見やすいかも。

$ ./linux-fincore -L -s /tmp/*
filename                                                                                       size        total_pages    min_cached page       cached_pages        cached_size        cached_perc
--------                                                                                       ----        -----------    ---------------       ------------        -----------        -----------
/tmp/access.log
size: 191,010
total_pages: 47
min_cached_page: -1
cached: 0
cached_size: 0
cached_perc: 0.00
/tmp/logs.sql.201305091141.gz
size: 8,908
total_pages: 3
min_cached_page: -1
cached: 0
cached_size: 0
cached_perc: 0.00
/tmp/logs.sql.201305131834
size: 72,382
total_pages: 18
min_cached_page: -1
cached: 0
cached_size: 0
cached_perc: 0.00
---
total cached size: 0

さあ、これでmroonga載っててswapが鬼の様なサーバー覗いてこよう。

2013/01/07

pt-ioprofileでMySQLのテンポラリテーブルのサイズを測る

Percona-Toolkitのうちの1つにpt-ioprofileというやつがあって、
straceでwriteやread, fsyncのシステムコール呼び出し状況を記録して、
lsofでファイルディスクリプタ番号と紐付けて、集計して出してくれたりする。

一番単純な使い方は多分こんな感じ。



# pt-ioprofile --cell sizes --run-time 60
2013年  1月  6日 日曜日 17:08:03 JST
Tracing process ID 1824
     total       read    pread64   pwrite64      write       open    _llseek filename
      2191          0       1024       1167          0          0          0 /var/lib/mysql/mysql/user.MYI
      1386          0       1386          0          0          0          0 /var/lib/mysql/mysql/proxies_priv.MYD
      1320          0       1320          0          0          0          0 /var/lib/mysql/mysql/db.MYD
       564        512         24         28          0          0          0 /var/lib/mysql/mysql/user.MYD
        81          0          0          0         81          0          0 /var/lib/mysql/bin.000056
         0          0          0          0          0          0          0 /var/lib/mysql/mysql/db.opt


デフォルトの計測時間は30秒。--run-timeで上書き。
デフォルトの集計はI/O`回数'。--cellでsizesを指定するとバイト単位のオペレーション量になる。

1分間でこれらのファイルにこれだけのread, writeが発生してた…というのが測れる。


さてさてさて。
pt-ioprofileを走らせて、おもむろに`converting HEAP to MyISAM'が走る様なクエリを流す。
(tmp_table_sizeを1024に設定して、DISK上に出させる)



# pt-ioprofile --cell sizes --run-time 10

2013年  1月  6日 日曜日 17:22:17 JST
Tracing process ID 4904
     total       read     pwrite      write       open      close      lseek filename
     80400      20100          0      20100          0          0      40200 /tmp/#sql_1328_0.MYD
      1782        328        126       1024          0          0        304 /tmp/#sql_1328_0.MYI



おお、取れた…んだけど、この値合ってんのかな。。
なんか違う気がする。



mysql> DESC t1;
+-------+---------------------+------+-----+---------+----------------+
| Field | Type                | Null | Key | Default | Extra          |
+-------+---------------------+------+-----+---------+----------------+
| num   | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| val   | char(32)            | YES  |     | NULL    |                |
+-------+---------------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

mysql> DESC t2;
+-------+---------------------+------+-----+---------+----------------+
| Field | Type                | Null | Key | Default | Extra          |
+-------+---------------------+------+-----+---------+----------------+
| num   | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| val   | char(32)            | YES  |     | NULL    |                |
+-------+---------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)


mysql> SELECT * FROM t2 LEFT JOIN t1 USING(num) ORDER BY t2.val DESC, t1.val ASC;
+-----+----------------------------------+----------------------------------+
| num | val                              | val                              |
+-----+----------------------------------+----------------------------------+
|  83 | fe9fc289c3ff0af142b6d3bead98a923 | fe9fc289c3ff0af142b6d3bead98a923 |
..
|  27 | 02e74f10e0327ad868d138f2b4fdd6f0 | 02e74f10e0327ad868d138f2b4fdd6f0 |
+-----+----------------------------------+----------------------------------+
100 rows in set (0.05 sec)

mysql> SHOW PROFILE;
+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.001830 |
| Waiting for query cache lock   | 0.000587 |
| checking query cache for query | 0.000904 |
| checking permissions           | 0.000647 |
| checking permissions           | 0.000546 |
| Opening tables                 | 0.001298 |
| System lock                    | 0.000778 |
| init                           | 0.000812 |
| optimizing                     | 0.004460 |
| statistics                     | 0.000866 |
| preparing                      | 0.000701 |
| Creating tmp table             | 0.001023 |
| executing                      | 0.000597 |
| Copying to tmp table           | 0.003121 |
| converting HEAP to MyISAM      | 0.015940 |
| Copying to tmp table on disk   | 0.005196 |
| Sorting result                 | 0.001541 |
| Sending data                   | 0.000519 |
| end                            | 0.000314 |
| removing tmp table             | 0.001926 |
| end                            | 0.000363 |
| query end                      | 0.000301 |
| closing tables                 | 0.000289 |
| freeing items                  | 0.006751 |
| logging slow query             | 0.000266 |
| cleaning up                    | 0.000558 |
+--------------------------------+----------+
26 rows in set (0.05 sec)



(BIGINT 4バイト + CHAR 32バイト) * 2テーブル結合 * 100行だから、
1行ごとのオーバーヘッド入れても20KiB近くも行かなそうなんだけどな。。

でもtmp_table_sizeを上げながら確認すると、
16KiBだとまだMyISAM変換されて、20KiBだとなくなる。

そんなものなのかぁ。


なおpt-ioprofileはstraceとlsofとpidof(だと思う。動き的に。中身は読んでない)を使ってるだけの
シェルスクリプトなので、


# pt-ioprofile --cell sizes --run-time 10 --profile-process rsyslogd
2013年  1月  6日 日曜日 17:25:44 JST
Tracing process ID 1227
     total      write filename
        46         46 /var/log/messages

こんな真似も可能だったりする。

ただし、
tpcc-mysqlで負荷かけながらmysqldにアタッチしてたら、
スレッドが何本かState: Tでハングって
mysqldが動かなくなったことがあるので、
絶対に本番でやってはダメです。



言われなくてもやりませんね、はい。。