BLOB против VARCHAR для хранения массивов в таблице MySQL

Мне нужно принять дизайнерское решение, и я ищу советы по передовой практике. У меня есть Java-программа, которой необходимо хранить большое количество (несколько сотен в день) массивов с плавающей запятой в базе данных MySQL. Данные представляют собой массив фиксированной длины Double длиной 300. Я вижу три разумных варианта:

  1. Сохраните данные как BLOB.
  2. Сериализуйте данные и сохраните их как VARCHAR.
  3. Запишите данные на диск в виде двоичного файла и вместо этого сохраните ссылку на него.

Я также должен упомянуть, что эти данные будут часто считываться и обновляться.

Я хочу использовать BLOB, так как это то, что я делал в прошлом, и это кажется наиболее эффективным методом (например, поддерживает фиксированную ширину и не нужно преобразовывать в строку, разделенную запятыми). Однако мой коллега настаивает на сериализации и использовании varchar по причинам, которые кажутся в основном догматическими.

Если один из этих методов лучше другого, являются ли причины специфичными для Java или MySQL?


person Doug    schedule 24.06.2010    source источник
comment
Чтобы на самом деле ответить на ваш конкретный вопрос о VARCHAR и BLOB: выберите BLOB! VARCHAR займет больше места и будет работать медленнее. Также будет неудобно работать (всегда конвертировать туда и обратно). Что касается этого обсуждения 300 ROWS против 1 ROW, как Билл Карвин показывает ниже: подход с 300 строками займет как минимум в 2 раза больше места и будет медленнее. Я также подозреваю, что будет более неудобно оперировать; т.е. вам нужно будет написать больше кода в вашем приложении, чтобы справиться с этим подходом. BLOB здесь хорош. И оставить работающий код в покое тоже хорошо.   -  person Julius Musseau    schedule 25.06.2010
comment
+2 Юлиусу за ответ на конкретный вопрос и +1 Биллу за совет   -  person Doug    schedule 25.06.2010


Ответы (4)


Сохраните как BLOB так (см. пример кода ниже). Я думаю, что это, вероятно, лучше, чем использование сериализации Java, поскольку для встроенной сериализации Java потребуется 2427 байт, а приложениям, отличным от Java, будет сложнее работать с данными. То есть, если в будущем когда-нибудь появятся какие-либо не-java-приложения, запрашивающие базу данных... если нет, то встроенная сериализация будет на несколько строк меньше.

public static void storeInDB() throws IOException, SQLException {

    double[] dubs = new double[300];

    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    DataOutputStream dout = new DataOutputStream(bout);
    for (double d : dubs) {
        dout.writeDouble(d);
    }
    dout.close();
    byte[] asBytes = bout.toByteArray();

    PreparedStatement stmt = null;  // however we normally get this...
    stmt.setBytes(1, asBytes);

}

public static double[] readFromDB() throws IOException, SQLException {

    ResultSet rs = null;  // however we normally get this...
    while (rs.next()) {
        double[] dubs = new double[300];
        byte[] asBytes = rs.getBytes("myDoubles");
        ByteArrayInputStream bin = new ByteArrayInputStream(asBytes);
        DataInputStream din = new DataInputStream(bin);
        for (int i = 0; i < dubs.length; i++) {
            dubs[i] = din.readDouble();
        }
        return dubs;
    }

}

Изменить: я надеялся использовать BINARY (2400), но MySQL говорит:

mysql> create table t (a binary(2400)) ;
ERROR 1074 (42000): Column length too big for column 'a' (max = 255);
use BLOB or TEXT instead
person Julius Musseau    schedule 24.06.2010
comment
Что, если вместо массива, хранящего значения в виде двойников, у нас есть массив строк? Работает ли тот же подход или есть лучший? - person niz; 27.06.2013

Есть ли причина, по которой вы не создаете дочернюю таблицу, чтобы вы могли хранить одно значение с плавающей запятой в строке вместо массива?

Скажем, вы храните тысячу массивов по 300 элементов каждый день. Это 300 000 строк в день или 109,5 млн в год. Не на что чихать, но в пределах возможностей MySQL или любой другой СУБД.


Повторите ваши комментарии:

Конечно, если порядок значителен, вы добавляете еще один столбец для порядка. Вот как я бы спроектировал таблицу:

CREATE TABLE VectorData (
  trial_id INT NOT NULL,
  vector_no SMALLINT UNSIGNED NOT NULL,
  order_no SMALLINT UNSIGNED NOT NULL,
  element FLOAT NOT NULL,
  PRIMARY KEY (trial_id, vector_no),
  FOREIGN KEY (trial_id) REFERENCES Trials (trial_id)
);
  • Общее пространство для строки векторных данных: 300x(4+2+2+4) = 3600 байт. Плюс каталог записей InnoDB (внутренние вещи) размером 16 байт.

  • Общее пространство, если вы сериализуете массив Java из 300 чисел с плавающей запятой = 1227 байт?

Таким образом, вы экономите около 2400 байт, или 67% пространства, сохраняя массив. Но предположим, что у вас есть 100 ГБ места для хранения базы данных. Хранение сериализованного массива позволяет хранить 87,5 миллионов векторов, тогда как нормализованный дизайн позволяет хранить только 29,8 миллионов векторов.

Вы сказали, что храните несколько сотен векторов в день, поэтому вы заполните этот 100-гигабайтный раздел всего за 81 год вместо 239 лет.


По поводу вашего комментария: производительность INSERT является важным проблема, но вы храните только несколько сотен векторов в день.

Большинство приложений MySQL могут выполнять сотни или тысячи вставок в секунду без чрезмерного волшебства.

Если вам нужна оптимальная производительность, обратите внимание на следующие моменты:

  • Явные транзакции
  • Синтаксис многострочного INSERT
  • INSERT DELAYED (если вы все еще используете MyISAM)
  • ЗАГРУЗИТЬ ФАЙЛ ДАННЫХ
  • ALTER TABLE DISABLE KEYS, сделать вставки, ALTER TABLE ENABLE KEYS

Найдите фразу «mysql вставляет в секунду» в своей любимой поисковой системе, чтобы прочитать множество статей и блогов, говорящих об этом.

person Bill Karwin    schedule 24.06.2010
comment
1.) Возможно, мне следовало сказать 300-мерный вектор вместо массива. Другими словами, порядок имеет значение, и нет абсолютно никаких причин обращаться к отдельному элементу без остальных. Кажется, что было бы немного медленнее извлекать и обрабатывать 300 строк. Я что-то упускаю? 2.) Мой догматичный коллега закатил бы истерику, если бы я сказал ему, что хочу добавлять 300 тысяч строк в таблицу каждый день. Ранее он серьезно возражал против одной из моих дизайнерских идей, потому что она предполагала ежедневное добавление 25 000 строк в кросс-таблицу. Есть ли у него смысл или я все равно должен это сделать? - person Doug; 24.06.2010
comment
Итак, еще раз, я, вероятно, должен был упомянуть, что проблема здесь не в хранении данных, а в скорости. Разве ваше решение не требует в 300 раз больше операций INSERT в таблицу с несколькими индексами? - person Doug; 24.06.2010

Если вы просто хотите сохранить данные в виде двоичного дампа массива Java, то, во что бы то ни стало, используйте BLOB. Ваш друг вполне может посоветовать против этого, поскольку вы можете захотеть, чтобы какая-то программа, отличная от Java, использовала информацию позже, поэтому бинарные дампы, вероятно, сложно интерпретировать.

Благодаря сериализации в VARCHAR вы знаете формат данных и можете легко прочитать его в любом приложении.

Конечно, если есть хоть малейшая вероятность того, что вы захотите манипулировать отдельными числами с плавающей запятой или создавать отчеты, они должны храниться в удобном для базы данных формате. Другими словами, не двоичный дамп, не сериализованный, не столбец CSV.

Сохраните их, как задумал Кодд, в третьей нормальной форме.

Кстати, несколько сотен 300-элементных массивов с плавающей запятой каждый день — это не большая база данных. Возьмите это от кого-то, кто работает на мэйнфреймах с DB2, большинство СУБД легко справятся с таким объемом. Мы ежедневно собираем десятки миллионов строк в наше приложение, и оно даже не вспотеет.

person paxdiablo    schedule 24.06.2010

Использование базы данных для хранения одномерного массива — это заноза в заднице! Еще больше с использованием rdm, где нет связи между хранимыми данными. извините, но лучшее решение, имхо, это использовать файл и просто писать данные так, как вам нравится. бинарный или как txt. Таким образом, 300xsize длины или 300x1 строка txt — это один массив.

person xstring    schedule 25.06.2010