ОЧЕНЬ огромная база данных SQL: как должна выглядеть схема? - PullRequest
6 голосов
/ 31 августа 2009

У меня есть 2 файла, которые я хотел бы импортировать в MS SQL. Первый файл имеет размер 2,2 ГБ, а второй - 24 ГБ. (если вам интересно: это справочная таблица по покеру)

Импортировать их в MS SQL не проблема. Благодаря SqlBulkCopy я смог импортировать первый файл всего за 10 минут. Моя проблема в том, что я не знаю, как должна выглядеть фактическая схема таблицы, чтобы позволить мне выполнять очень быстрые запросы. Моя первая наивная попытка выглядит так:

CREATE TABLE [dbo].[tblFlopHands](
    [hand_id] [int] IDENTITY(1,1) NOT NULL,
    [flop_index] [smallint] NULL,
    [hand_index] [smallint] NULL,
    [hs1] [real] NULL,
    [ppot1] [real] NULL,
    [hs2] [real] NULL,
    [ppot2] [real] NULL,
    [hs3] [real] NULL,
    [ppot3] [real] NULL,
    [hs4] [real] NULL,
    [ppot4] [real] NULL,
    [hs5] [real] NULL,
    [ppot5] [real] NULL,
    [hs6] [real] NULL,
    [ppot6] [real] NULL,
    [hs7] [real] NULL,
    [ppot7] [real] NULL,
    [hs8] [real] NULL,
    [ppot8] [real] NULL,
    [hs9] [real] NULL,
    [ppot9] [real] NULL,
 CONSTRAINT [PK_tblFlopHands] PRIMARY KEY CLUSTERED 
(
    [hand_id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Индекс флопа - это значение от 1 до 22100 (первые 3 общие карты в техасском холдеме, 52 выбирают 3). Каждый индекс флопа имеет индекс hand_index от 1 до 1176 (49 выбирают 2). Таким образом, в этой таблице 25 989 600 строк.

Выполнение запроса с моей приведенной выше «схемой» заняло ок. 25 секунд После некоторого поиска в Google я обнаружил, что сервер SQL выполняет сканирование таблицы, что, очевидно, является плохой вещью. Я запустил «Помощник по настройке ядра СУБД», и он порекомендовал создать индекс для столбца flop_index (имеет смысл). После создания индекса необходимые дисковые пространства для БД удвоились! (плюс файл журнала LDF вырос на 2,6 ГБ) Но после индексации запрос занял всего пару мс.

Теперь мой вопрос: как мне сделать это правильно? Я никогда не работал с такими массивными данными, базы данных, которые я создал ранее, были шуткой.

Некоторые вещи, на которые следует обратить внимание: после импорта данных в MS SQL никогда не будет вставки или обновления данных, просто выберите их. Поэтому мне интересно, нужен ли мне хотя бы первичный ключ?

РЕДАКТИРОВАТЬ: Я предоставляю дополнительную информацию, чтобы сделать мой вопрос более ясным:

1) Я никогда не буду использовать hand_id. Я положил его туда только потому, что кто-то давным-давно сказал мне, что я должен всегда создать первичный ключ для каждой таблицы.

2) Я буду использовать только один запрос:

SELECT hand_index, hs1, ppot1, hs2, ppot2, hs3, ppot3, hs4, ppot4, hs5, ppot5, hs6, ppot6, hs7, ppot7, hs8, ppot8, hs9, ppot9 WHERE flop_index = 1...22100

Этот запрос всегда будет возвращать 1176 строк с нужными мне данными.

EDIT2: Просто чтобы быть более конкретным: да, это статические данные. У меня есть эти данные в двоичном файле. Я написал программу для запроса этого файла с данными, которые мне нужны всего за несколько миллисекунд. Причина, по которой мне нужны эти данные в базе данных, заключается в том, что я хочу иметь возможность запрашивать данные с разных компьютеров в моей сети без необходимости копировать 25 ГБ на каждый компьютер.

HS означает ручную силу, он показывает текущую силу руки ваших закрытых карт в сочетании с картами флопа или терна. ppot означает положительный потенциал, это шанс, что ваша рука будет впереди после раздачи следующей общей карты. hs1 до 9 - это сила руки против 1 до 9 противников. То же самое для ppot. Вычисление ppot на лету требует очень много времени и занимает несколько минут. Я хочу создать программу анализа покера, которая дает мне список всех возможных комбинаций хоул-карт на любом флопе / ходу с их hs / ppot.

Ответы [ 5 ]

1 голос
/ 31 августа 2009

Ну, вы можете разбить таблицу на более мелкие таблицы, если, например, hs (X) и ppot (X) должны вырасти после девяти.

Вот что у вас есть:

[hand_id] [int] IDENTITY(1,1) NOT NULL,
    [flop_index] [smallint] NULL,
    [hand_index] [smallint] NULL,
    [hs1] [real] NULL,
    [ppot1] [real] NULL,
    etc...

Вы можете разбить его на 2 таблицы (возможно, 3, если вам нужно)

Table hand: (EXAMPLE)
[hand_id] [int] IDENTITY(1,1) NOT NULL,
    [flop_index] [smallint] NULL,
    [hand_index] [smallint] NULL


Table hs_ppot (EXAMPLE)
[hand_id] [int] IDENTITY(1,1) NOT NULL,
[hs] [real] NULL,
    [ppot] [real] NULL

Тогда вы можете ссылаться на hand_id в каждой таблице. Просто хоть.

Кстати, что такое hs и ppot?

1 голос
/ 31 августа 2009

Чтобы ответить на ваш вопрос о необходимости использования первичного ключа, используйте только ту информацию, которую вы указали в вопросе:

Исходя из вашей схемы таблиц, вы также можете хранить ее там. Если вы удалите этот столбец идентификаторов, вы также удалите кластерный индекс. Ваше значение кластеризованного индекса (4 байта) сохраняется как указатель в каждой строке некластеризованного индекса. Удалив этот кластеризованный индекс, вы оставите таблицу в виде кучи - и SQL создаст 8-байтовый RID (идентификатор строки) для каждой строки в таблице и вместо этого будет использовать его в качестве указателя в некластеризованном индексе. Итак, в вашем случае, исходя из схемы, которую вы указали в вопросе, вы потенциально можете УВЕЛИЧИТЬ размер своих некластеризованных индексов и в конце концов замедлить их.

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

0 голосов
/ 01 сентября 2009

Позвольте мне предвосхитить мой ответ, сказав, что помещать каждую возможную комбинацию в базу данных кажется неправильным. Я выясню почему через минуту.

Я бы начал со стола под названием Карты. Для каждой возможной карты будет 1 запись, и она будет включать в себя поля для масти, номинальной стоимости, ранга и да, идентификатор карты в качестве первичного ключа. Также индексируйте костюм и номинал.

Если вы хотите составить таблицу каждой возможной руки в Холдеме, я бы сделал отдельные таблицы для pocketCards (pocketID, pCardID1, pCardID2), flopCards (flopID, fCardID1, fCardID2, fCardID3), а затем таблицы для TurnAndRiver ( turnAndRiverID, turnCardID, riverCardID). Затем таблица рук с (handID, pocketID, flopID, turnAndRiverID, handScore).

HandScore будет вычисляемым полем из таблицы или функции скалярного значения.

Разделяя эти биты, вы избегаете большого количества дублирования, но вам все равно придется беспокоиться о выборе карт и их перекрытии.

В идеале я бы отказался от таблиц рук и вычислял руку и счет в любом приложении, которое я собирал, чтобы использовать эти данные.

Из-за слишком большой логики вашей базы данных может возникнуть проблема с адаптацией, когда клиент просит вас, например, моделировать Омаху или пятикарточный дро.

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

Обновление

В ответ на правку ОП: Похоже, вы используете не тот инструмент для этой задачи. Какова ценность данных в базе данных, если вы всегда будете выбирать один и тот же набор записей? Изучите другие варианты (например, плоский XML-файл или статический DataSet в вашем коде). Это сэкономит вам время соединения и затраты на работу сервера для статических данных.

0 голосов
/ 31 августа 2009

Как вы делаете ваши индексы и примки зависит. Если вы просто хотите проанализировать данные и уверены, что последующими командами DML будут только SELECT (без INSERT), тогда удаление PK должно подойти. Фактически, столбец hand_id является столбцом IDENTITY (автоинкремент), что означает, что SQL Server все равно управляет этим значением (фактически, вы не можете вставить значения в этот столбец, не вдаваясь в дополнительные проблемы с включением режима IDENTITY_INSERT до начиная с заявления INSERT, IIRC).

Будьте осторожны с меняющимися потребностями в этой базе данных, конечно. В случае необходимости изменения следует учитывать ограничения / индексы / ключи.

Если в будущем планируется анализ данных, рассмотрите возможность использования Microsoft SSAS (службы Analysis Services).

ОБНОВЛЕНИЕ: после прочтения ответа Мейо я согласен с тем, что индексы (исключительно для скорости, а не для принудительного применения ограничений) рекомендуются для последующих запросов (напомним, что индексы ускоряют операции чтения, но обычно вставки / обновления занимают больше времени). Поскольку ваша цель - выполнить одну массовую вставку, а затем выполнить запросы SELECT, вы можете выполнить массовую вставку, а затем добавить необходимые индексы в базу данных по столбцам, которые, вероятно, являются кандидатами в ваших запросах.

0 голосов
/ 31 августа 2009

Это очень распространенный вопрос. При создании индексов это потенциально уменьшает время, необходимое для запросов, но увеличивает время, необходимое для обновлений / вставок, а также увеличивает объем дискового пространства, требуемого для записи.

Вам необходимо решить для каждого столбца, будет ли индекс повышать производительность ваших запросов и будет ли это влиять на производительность вставки / обновления и использование дискового пространства.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...