MySQLのVIEWでできること・できないこと

旅行メディアシステム部のサエキと申します。 本記事はMySQLのVIEWに助けられた体験の紹介とVIEWに対する各種操作の可否をまとめたものです。

MySQLのVIEWに助けられた話

先日担当しているサービスのMySQLのDB群を5.7から8.0へアップグレードし、一部をInnoDBクラスターに切り替えました。

メインシステムのDB(以下メインDB)から一部テーブルをレプリケーションフィルターを使ってサブシステムのDB(以下サブDB)に連携していたのですが、その対象テーブルはテーブル名が大文字でした。ただDBアップグレード作業でメインDBをInnoDBクラスターに切り替える計画でした。

InnoDBクラスターは Group Replication, MySQL router, MySQL Shell を組み合わせた高可用性ソリューションです。こちらGroup Replication の要件から一部を引用します。

小文字のテーブル名. すべてのグループメンバーで --lower-case-table-names を同じ値に設定します。 グループレプリケーションに必要な InnoDB ストレージエンジンを使用するには、1 の設定が適切です。 この設定は、すべてのプラットフォームでデフォルトであるわけではありません。

新メインDBではテーブル名が小文字に切り替わります。よってサブDBのレプリケーションソースを新メインDBに切り替える際には、サブDBのレプリケーションフィルターとテーブル定義のテーブル名を小文字化するレプリケーション再構築が必要になります。

メインDBでは lower_case_table_names = 1 のため大文字テーブル名で参照しても問題ありませんが、サブDBでは同設定ができない状況でした。このままではサブDBの小文字になってしまったテーブルを参照する処理をすべて書き換える必要があり、テーブル名変更と参照アプリケーションの修正リリースがきっかり同時にできることもなく多少のエラーを引き起こしてしまいます。

こういったテーブル名変更とアプリ側の切り替わりを同タイミングでできない場合には、VIEWを使って旧テーブル名への参照を新テーブルにつなげることで既存アプリケーションを修正せずに済ませることができます。

before_tableafter_table に切り替えるフローを以下に示します。

create table after_table like before_table;
create view void_view as select * from after_table;
drop table after_table; -- 次のrenameまで void_view への参照は ERROR 1356 を起こします
rename table before_table to after_table, void_view to before_table;

過去にもVIEWでエイリアスを作るような同様の対処はしていましたが、Case Insensitiveな人間(やWindows)からすると同名の大文字のエイリアスを作ることになったのはちょっと面白い体験でした。

エイリアスのようなVIEWでできること、できないこと

今回のケースではレプリカ側の参照なので更新することはないのですが、一部のVIEWは更新もできます。 エイリアスのようなVIEWに対して、TABLE同様の操作は可能なのか、できないときはどのようなメッセージが出るのかいくつかの操作で確認してみました。検証バージョンは8.0.28です。

select

できます。むしろできないと困りますね。select自体が失敗する場合は参照オブジェクトが存在するか、必要な権限があるかを確認しましょう。

insert(replace) delete update

できます。 VIEWの定義によってはDMLを実行できません。集約、集計、抽出を目的として作られる一般的なVIEWだと以下のようなエラーメッセージが出ます。

ERROR 1471 (HY000): The target table view_name of the INSERT is not insertable-into
ERROR 1288 (HY000): The target table view_name of the UPDATE is not updatable
ERROR 1288 (HY000): The target table view_name of the DELETE is not updatable

load data into table

できます。

lock tables

できます。

truncate table

できません。

mysql> truncate table view_name;
ERROR 1146 (42S02): Table 'sample.view_name' doesn't exist

analyze optimize

できません。

mysql> optimize table view_name;
+------------------+----------+----------+--------------------------------------+
| Table            | Op       | Msg_type | Msg_text                             |
+------------------+----------+----------+--------------------------------------+
| sample.view_name | optimize | Error    | 'sample.view_name' is not BASE TABLE |
| sample.view_name | optimize | status   | Operation failed                     |
+------------------+----------+----------+--------------------------------------+
2 rows in set (0.01 sec)

alter table

できません。

mysql> alter table view_name add key value_idx(value);
ERROR 1347 (HY000): 'sample.view_name' is not BASE TABLE

rename table

できます。

mysql> rename table view_name to view_name_after;
Query OK, 0 rows affected (0.01 sec)

alter table 構文ではできません。

mysql> alter table view_name rename to view_name_after;
ERROR 1347 (HY000): 'sample.view_name' is not BASE TABLE

show tables

TABLEと同様に出力されます。

mysql> show tables like '%_name';
+---------------------------+
| Tables_in_sample (%_name) |
+---------------------------+
| table_name                |
| view_name                 |
+---------------------------+
2 rows in set (0.00 sec)

TABLEのみ、VIEWのみを取得したい場合はinformation_schemaから取得しましょう。

show create table

できます。 ただしVIEWに対する show create tableshow create view を実行します。

mysql> show create table view_name \G
*************************** 1. row ***************************
                View: view_name
         Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `view_name` AS select `hoge`.`id` AS `id`,`hoge`.`value` AS `value` from `hoge`
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

逆にTABLEに対して show create view を実行するとちゃんとエラーになります。

mysql> show create view table_name;
ERROR 1347 (HY000): 'sample.table_name' is not VIEW

drop table

できません。

mysql> drop table view_name;
ERROR 1051 (42S02): Unknown table 'sample.view_name'

create table like

できません。

mysql> create table view_copy_table like view_name;
ERROR 1347 (HY000): 'sample.view_name' is not BASE TABLE

まとめ

エイリアスのようなVIEWでできること・できないことをまとめたものが以下です。

statement executable
select yes
insert yes
replace yes
update yes
delete yes
load data into yes
lock yes
truncate no
analyze no
optimize no
alter no
rename yes
show tables yes
show create yes
drop no
create table like no

バッチジョブのような処理だと困るケースもありそうですが、Webアプリケーションから利用するケースであればVIEWに切り替えても問題が起きないで済みそうです。 上記制限をクリアしたうえでアプリケーションの参照を新しいテーブル名に切り替えるのを忘れないといった用法用量を守って正しくお使いください。

あとがき

レプリケーションで繋がっている複数DB群の移行は、参照しているアプリケーションも複数あり複雑なため数日かかりました。 その最中に8.1のリリースやリリースモデルの変更がアナウンスされました。次の移行時にはバージョン選定がしやすくなるなと期待しています。

カカクコムでは、ともにサービスをつくる仲間を募集しています!

カカクコムのエンジニアリングにご興味のある方は、ぜひこちらをご覧ください!

カカクコム採用サイト