SQLDatabaseとMyBatisによりスロークエリが発生する問題を解消した

JDBCを利用したORマッパーのMyBatisとSQLDatabaseの組み合わせで、パフォーマンス

問題(スロークエリ)が発生し、解決まで導いたので、メモ。

問題は、char、varcharのフィールドに対する暗黙の型変換と文字コードが悪さをしていた。

MyBatisからSQLServerやSQLDatabaseを利用する際、設定を変更するか、DBの型をncharやnvarcharを使用しましょうという教訓です。

 

バージョン

・Java:1.8.0_242, vendor: AdoptOpenJDK,

・SpringBoot:2.2.4

・SQLDatabase:15.0.2000.2107

・mybatis-spring-boot-starter:2.1.1

 

ちょっと古いバージョンだったりするのだが、バージョンが上がっても、この問題にはぶつかる可能性は大いにある。

 

事象

1.対象テーブルの件数が20万件くらいから、スロークエリが発生

2.Where句に特定の条件を指定すると、30秒以上かかってしまい、クエリタイムアウトしてしまう。

3.Where句で使用するフィールドにはインデックスが貼ってあり、利いている状態。

4.DTUを調整したり、プランを上げたり、対応をしたがそれでも発生する。

5.アプリから実行するとタイムアウト(結果の返却に30秒以上)するが、sqlcmdを利用

して直接想定するクエリを実行すると約2秒くらいしかかからない。

6.Where句で指定するテーブルのフィールドの型はcharで、「A001」のような英数混合のコード値を保持している

 

データ件数が増えてきて、問題が発生したのだが、実行計画を見ると遅くなりそうな問題点はなかった。

そして、一番の問題点は、5のアプリからしか問題が発生せず、想定するクエリ(これ自体は正しい)を叩くと早いという、アプリやORマッパーの問題に見える点。
実際の問題は、JDBCとMyBatisの仕様に対して、charやvarcharの文字コードの問題だった。

 

解決の糸口

a.実行しているSQLを確認すると、プレースホルダーに対する変数の文字列(@が付いた変数)に対して、nvarcharと書いてあった。
→本来であれば、charやvarcharくらいなら分かるのだが、nがついているのは違和感。

b.charのフィールドに対して、charにするようにMyBatisの設定を確認したが、設定できるようなものはない。
→ORマッパーを使わず、PreparedStatamentを利用していたりすれば、文字列に対して、setStringやSetNStringという型の指定ができるのだが、MyBatisにはそのような設定がなかった

c.SQLServerのJDBC(SQLDatabaseでもSQLServerのJDBCを使う)の文字列の初期設定が、sendStringParametersAsUnicode=trueであるということが判明
→つまり、指定された文字列は基本Unicodeで扱うということ。Unicodeを使うなら、DBのテーブルのフィールドの型はncharやnvarcharとなる

 

aで使用したSQL 最近の10件を確認できるSQL

SELECT TOP(10)
,st.text
,last_execution_time
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
ORDER BY last_execution_time DESC

上記のことから、charとnvarcharの型不一致の問題ではないかという仮説を立てた。

対処方法は3つ考えられた。

①クエリにCASTを付ける

②sendStringParametersAsUnicode=falseにする

③該当テーブルのフィールドの型をcharからncharに変更する

 

結果的には、③を実行した。

①はクエリの修正となり、MyBatisのxmlファイル内の記述の変更を行うことになる。そして、リリース作業が必要となるので、作業コストがかかる。

②は全体に影響を与えてしまうため、問題のクエリ以外の全部のクエリをテストしないといけない。そして、リリース作業も必要となり、この中では影響範囲もコストも高くて、却下。

③は型の変更のみ。さらに、charからncharで、英数しか入らないフィールドなのは確定だったため、文字化けがしないかくらいの確認くらいで作業が済む。

これで、タイムアウトしていた、クエリが3秒くらいで返ってくるようになったので、まあまあ想定の範囲内となったかなと。

MyBatisを使うのならば、設計の時点で、charやvarcharは使わず、ncharやnvarcharのみでも良いかもしれない。

 

絵で見てわかるSQL Serverの仕組み

絵で見てわかるSQL Serverの仕組み

  • 作者:平山 理
  • 発売日: 2020/09/14
  • メディア: Kindle版