MySQL中,Boolean只是 tinyint(1) 的別名,也就是說,MySQL中並沒有真正的bool類型。
而SQLAlchemy生成SQL的時候並沒有檢測到 這一點,這就導致一個問題,當使用 bool 類型作為查詢條件時,用不上索引,從而導致掃表的行為:

> SELECT COUNT(*) FROM message WHERE message.is_national = 1 AND message.updated_at > '2020-01-01 00:00:00' AND message.deleted_at IS NULL; +----------+ | COUNT(*) | +----------+ | 0 | +----------+ 1 row in set Time: 0.018s > SELECT COUNT(*) FROM message WHERE message.is_national is true AND message.updated_at > '2020-01-01 00:00:00' AND message.deleted_at IS NULL; +----------+ | COUNT(*) | +----------+ | 0 | +----------+ 1 row in set Time: 2.162s
注意觀察第一行和第二行的時間,很明顯第二行沒有用上索引,我們來看看 EXPLAIN 的結果便知道了:

> EXPLAIN SELECT COUNT(*) FROM message WHERE message.is_national = 1 AND message.updated_at > '2020-01-01 00:00:00' AND message.de leted_at IS NULL; | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | | 1 | SIMPLE | message | ref | ix_message_updated_at,idx_updated_at_is_national,ix_message_is_national | ix_message_is_national | 1 | const | 1 | Using where |> EXPLAIN SELECT COUNT(*) FROM message WHERE message.is_national is true AND message.updated_at > '2020-01-01 00:00:00' AND messag
e.deleted_at IS NULL;
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | SIMPLE | message | ALL | ix_message_updated_at,idx_updated_at_is_national | <null> | <null> | <null> | 一個很大的數字 | Using whe
re |
mysql文檔給出的解釋
java.lang.Boolean if the configuration property tinyInt1isBit is set to true (the default) and the storage size is 1, or java.lang.Integer if not.
要注意下面這個提示
The ResultSet.getObject()
method uses the type conversions between MySQL and Java types, following the JDBC specification where appropriate. The values returned by ResultSetMetaData.GetColumnTypeName()
and ResultSetMetaData.GetColumnClassName()
are shown in the table below. For more information on the JDBC types, see the reference on the java.sql.Types class.
文檔地址:https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-type-conversions.html
解決方案:
1.使用ifnull(column, 0)處理該字段,個人測試過可以;
2.在JDBC的URL增加 tinyInt1isBit=false參數,注意參數名區分大小寫,否則不生效(默認為true)
即:jdbc:mysql://${ucmha.proxy1_2.host}/${db.mysql.db}?tinyInt1isBit=false
3.避免使用長度為1的tinyint類型字段存儲數字格式的數據;
參考資料:
- https://jiajunhuang.com/articles/2020_03_06-mysql_boolean_tinyint_index.md.html
- https://dev.mysql.com/doc/refman/8.0/en/numeric-type-syntax.html
- https://www.jianshu.com/p/6885cad1cb14/
- https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-type-conversions.html