BLOB против VARCHAR для хранения массивов в таблице MySQL - PullRequest
16 голосов
/ 24 июня 2010

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

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

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

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

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

Ответы [ 4 ]

14 голосов
/ 24 июня 2010

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

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


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

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

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 вставок в секунду» в своей любимой поисковой системе, чтобы прочитать много статей и блогов, говорящих об этом.

8 голосов
/ 24 июня 2010

Хранить как большой двоичный объект (см. Пример кода ниже).Я думаю, что это, вероятно, лучше, чем использование 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
3 голосов
/ 24 июня 2010

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

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

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

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

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

0 голосов
/ 26 июня 2010

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

...