我使用的是Rails 3.2.6和Mysql 6.0.9(但我在Mysql 5.2.25上有完全相同的错误)
当我创建新的数据库(rake db:create
),然后当我尝试加载模式(rake schema:load
)时,我会得到以下错误:
Mysql2::Error: Specified key was too long; max key length is 767 bytes: CREATE UNIQUE INDEX `unique_schema_migrations` ON `schema_migrations` (`version`)
经过数小时的研究,我找到了以下解决方案:
1.将MySQL变量innodb_large_prefix更改为true(或ON)
这没用。我在Linux服务器、Mac甚至Windows上都试过了,但都不起作用。
2.Monkeypatch ActiveRecord::SchemaMigration.create_table
我不需要version
列的长度为255(当它是UTF-8时,它需要4*255=1020个字节,并且超过了MySQL对密钥767个字节的限制)。我也不需要它是UTF-8,但DB中的所有其他表都是UTF-8,并且我已经将utf8_czech_ci设置为默认排序规则。
实际创建schema_mmigrations表的方法如下所示:
def self.create_table
unless connection.table_exists?(table_name)
connection.create_table(table_name, :id => false) do |t|
t.column :version, :string, :null => false
end
connection.add_index table_name, :version, :unique => true, :name => index_name
end
end
你可以在Github rails/rails 上读取整个文件
因此,我尝试将:limit => 100
添加到t.column
语句中,但我也没有成功使用此解决方案。问题是,当原始补丁已经就位时,我无法加载此补丁。换句话说,我的补丁在ActiveRecord::SchemaMigration之前加载,因此它被覆盖。
当我把这个放在config/initializers/patches/schema_migration.rb
:中时
require 'active_record/scoping/default'
require 'active_record/scoping/named'
require 'active_record/base'
module ActiveRecord
class SchemaMigration < ActiveRecord::Base
def self.create_table
unless connection.table_exists?(table_name)
connection.create_table(table_name, :id => false) do |t|
t.column :version, :string, :null => false, :limit => 100
end
connection.add_index table_name, :version, :unique => true, :name => index_name
end
end
end
end
它已成功加载,但在加载原始ActiveRecord::SchemaMigration时会覆盖它。
我试着把ActiveSupport.onload(:active_record)搞砸了,但这似乎也不起作用。
有没有办法在原始ActiveRecord::SchemaMigration到位后加载此文件并使此补丁正常工作
你有什么建议吗?我可以澄清这个问题的任何部分,如果它对你没有意义的话。问问我就知道了。我已经被这件事困扰太久了。
767键应该可以工作。请确保使用utf8
编码,而不是utf16
。我也遇到了同样的问题,我的错误是意外地创建了utf16
数据库
我建议您删除数据库,并按照以下说明重新创建一个新数据库:
mysql -u root -p -e "CREATE DATABASE {DB_NAME} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;"
我对长度为2000 的varchar的列命名版本也有同样的问题
class AddVersionToUsers < ActiveRecord::Migration
def change
add_column :users, :version, :string, limit:2000
add_index :users, :version
end
end
我使用的是拉丁语1 1个字符1个字节,但现在我想使用utf8mb4 1个字符4个字节。
这样配置你的数据库,你可以得到索引,直到3072字节:
docker run -p 3309:3306 --name test-mariadb -e MYSQL_ROOT_PASSWORD=Cal1mero. -d mariadb:10.2 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --innodb-large-prefix=1 --innodb-file-format=barracuda --innodb-file-per-table=1 --innodb-strict-mode=1 --innodb-default-row-format=dynamic
这对于latin1来说已经足够了(将是2000字节),但是对于utf8mb4来说将是8000字节。在这个钥匙你有一些选择
添加一个名为hash_version的列,并在该列上实现索引。
一致字符串#仅基于字符串的哈希';s内容
缩短字符串,它应该可以工作,但取决于您的需求
或者在迁移中使用全文,如下所示:
class AddVersionToUsers < ActiveRecord::Migration
def change
add_column :users, :version, :string, limit:2000
add_index :users, :version, type: :fulltext
end
end
参考:
- https://mensfeld.pl/2016/06/ruby-on-rails-mysql2error-incorrect-string-value-and-specified-key-was-too-long/
- https://codex.wordpress.org/Converting_Database_Character_Sets
- https://dev.mysql.com/doc/refman/8.0/en/innodb-restrictions.html
- https://docs.oracle.com/cd/E17952_01/mysql-5.7-en/innodb-restrictions.html
- https://dba.stackexchange.com/questions/35821/possible-index-on-a-varchar-field-in-mysql