创建schema_migrations的Rails - Mysql2 ::错误:指定的键太长了

时间:2012-07-25 07:34:41

标签: mysql ruby-on-rails activerecord rails-migrations

我正在使用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个字节并且超过了密钥的767字节的MySQL限制)。我也不需要它是UTF-8,但DB中的所有其他表都是UTF-8,我已将utf8_czech_ci设置为默认排序规则。

实际创建schema_migrations表的方法如下所示:

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.on_load(:active_record),但这似乎也不起作用。

有没有办法在原始ActiveRecord :: SchemaMigration到位后加载此文件并使此补丁有效?

你有什么建议吗?如果对你没有任何意义,我可以澄清这个问题的任何部分。尽管问我。我已经坚持了这么久。

4 个答案:

答案 0 :(得分:8)

767键应该可以工作。确保使用utf8编码,而不是utf16。 我有同样的问题,我的错误是我意外地创建了utf16数据库

答案 1 :(得分:3)

我建议您删除数据库并使用以下说明重新创建一个新数据库:

# A tibble: 16 × 4
      gr   sum  rows y_value
   <dbl> <dbl> <int>   <dbl>
1      1    22     4       0
2      1    22     4       0
3      1    22     4      10
4      1    22     4       8
5      1    22     4       8
6      1    22     4       6
7      1    22     4       0
8      2    21     6       0
9      2    21     6       0
10     2    21     6      10
11     2    21     6      10
12     2    21     6       5
13     2    21     6       4
14     2    21     6       2
15     2    21     6       0
16     2    21     6       0

答案 2 :(得分:0)

我总是有这个错误,当它不是这个错误时:

Mysql2 ::错误:指定的密钥太长;最大密钥长度为767字节:

是另一个:

Mysql2 ::错误:指定的密钥太长;最大密钥长度为3072字节

所以我按照建议here使用docker和官方mysql创建一个具有此设置的新数据库

docker run -p 3306:3306 --name mysql-name -e MYSQL_ROOT_PASSWORD=name -d mysql:5.6 --innodb-large-prefix=1 --innodb-file-format=barracuda --innodb-file-per-table=1

这里的重要事情不是docker是数据库啊配置

<强> innodb的大型前缀=真 innodb的文件格式=梭鱼 innodb的文件每次表=真

最后data.yml如下更改utf8 latin1 sswedish是因为在服务器中我看到了:

development:
  host: 192.168.99.100
  database: name
  username: root
  password: root
  adapter: mysql2
  charset: latin1
  encoding: latin1
  collation: latin1_swedish_ci


test:
  host: 192.168.99.100
  database: name_test
  username: root
  password: root
  adapter: mysql2
  charset: latin1
  encoding: latin1
  collation: latin1_swedish_ci

production:
  host: 192.168.99.100
  database: name
  username: root
  password: root
  adapter: mysql2
  charset: latin1
  encoding: latin1
  collation: latin1_swedish_ci

之后

bundle exec rake db:create
bundle exec rake db:migrate

我是用猴子补丁完成的,没有建议here的猴子补丁,它可以正常工作

config/initializers/ar_innodb_row_format.rb

ActiveSupport.on_load :active_record do
  module ActiveRecord::ConnectionAdapters   
    class AbstractMysqlAdapter 
      def create_table_with_innodb_row_format(table_name, options = {}) 
        table_options = options.reverse_merge(:options => 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC')

        create_table_without_innodb_row_format(table_name, table_options) do |td|
         yield td if block_given? 
        end
      end
      alias_method_chain :create_table, :innodb_row_format 
    end
  end
end

答案 3 :(得分:0)

对于长度为2000的varchar的名为version的列,我也有同样的问题

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

这对于latin_1足够了(将为2000字节),但对于utf8mb4则将为8000字节。在这些键中,您有一些选择

添加一个名为hash_version的列,并在该列上实现索引。

Consistent String#hash based only on the string's content

使字符串更短,它应该可以工作,但是会根据您的需要

或在迁移过程中使用全文,如下所示:

class AddVersionToUsers < ActiveRecord::Migration
  def change
    add_column :users, :version, :string, limit:2000
    add_index  :users, :version, type: :fulltext
  end
end

参考: