Apache James 使用mysql存儲啟動報錯Specified key was too long; max key length is 3072 bytes
沒事搗鼓自建mail服務,作為java開發,肯定想到了java實現。剛好apache就有開源的James(Java Apache Mail Enterprise Server)。下載下來准備試試。
目前最新版是 3.4.0版本。
下載完成后網上找到教程進行配置。
因為針對mysql報錯的問題。只記錄數據源配置點。
修改數據源
修改conf/james-database.properties中
# Use derby as default
# database.driverClassName=org.apache.derby.jdbc.EmbeddedDriver
# database.url=jdbc:derby:../var/store/derby;create=true
# database.username=app
# database.password=app
database.driverClassName=com.mysql.jdbc.Driver
database.url=jdbc:mysql://127.0.0.1:3306/james?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8
database.username=james#賬密自行修改即可
database.password=james
# Supported adapters are:
# DB2, DERBY, H2, HSQL, INFORMIX, MYSQL, ORACLE, POSTGRESQL, SQL_SERVER, SYBASE
# vendorAdapter.database=DERBY
vendorAdapter.database=MYSQL
因為教程中提示可能會報錯,我提前在數據庫中執行了下面腳本。
SET GLOBAL innodb_file_format = BARRACUDA;
SET GLOBAL innodb_large_prefix = ON;
報錯
在bin目錄下執行run.bat
(win10)。
啟動報錯。
org.apache.openjpa.persistence.PersistenceException: Specified key was too long; max key length is 3072 bytes {stmnt 2076356118 CREATE TABLE JAMES_MAIL_REPOS (MAIL_REPO_NAME VARCHAR(1024) NOT NULL, PRIMARY KEY (MAIL_REPO_NAME)) ENGINE = innodb} [code=1071, state=42000]
分析
這個問題和教程中提到的類似,網上查找是MySQL主鍵索引長度不夠。
找到很多資料后得知:mysql默認索引長度最大長度是767bytes,如果是utf8編碼(MySQL的utf8編碼最長3byte)。只能用varchar(255)。開啟innodb_large_prefix=on
后索引長度最大長度是3072bytes,如果是utf8編碼。應該能用varchar(1024)。而日志中的sql顯示字段長度為1024。理論上不應該報錯。
解決
問題就在這編碼上,按照上面的邏輯,字段長度乘以編碼最大字節長度小於等於3072就可以。但是我使用的utf8mb4編碼(MySQL獨有的,最長4byte)。所以varchar(1024)最長就是4096>3074。我刪除原來的數據庫,以utf8重新創建,啟動不再報錯。
這里要吐槽一下,其實uft8編碼字符理論上可以最多到6個字節長,通常uft8使用1~4字節為每個字符編碼(utf8是變長編碼-霍夫曼編碼)。但是MySQL最開始支持utf8的時候只做了最長3個字節(MySQL中的uft8)。后面為了兼容4個字節,又增加了uft8mb4(舊版本沒有)。所以其實MySQL中utf8mb4才是真正的utf8。之前公司項目中為了支持emoji表情(4字節utf8編碼),了解過MySQL的這段編碼故事,所以MySQL中喜歡使用utf8mb4。結果就遇到了這個坑爹的問題。