Поля MySQL и INT auto_increment - PullRequest
3 голосов
/ 26 мая 2010

Я занимаюсь разработкой в ​​LAMP (Linux + Apache + MySQL + PHP), так как я себя помню. Но один вопрос беспокоил меня уже много лет. Я надеюсь, что вы можете помочь мне найти ответ и указать мне правильное направление. Вот мой вызов:

Скажем, мы создаем веб-сайт сообщества, где мы разрешаем нашим пользователям регистрироваться. Таблица MySQL, в которой мы храним всех пользователей, выглядела бы тогда так:

CREATE TABLE `users` (
  `uid` int(2) unsigned NOT NULL auto_increment COMMENT 'User ID',
  `name` varchar(20) NOT NULL,
  `password` varchar(32) NOT NULL COMMENT 'Password is saved as a 32-bytes hash, never in plain text',
  `email` varchar(64) NOT NULL,
  `created` int(11) unsigned NOT NULL default '0' COMMENT 'Timestamp of registration',
  `updated` int(11) unsigned NOT NULL default '0' COMMENT 'Timestamp of profile update, e.g. change of email',
  PRIMARY KEY  (`uid`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

Итак, из этого фрагмента видно, что у нас есть уникальное и автоматически увеличивающееся поле для каждого нового пользовательского поля 'uid'. Как и на каждом хорошем и лояльном веб-сайте сообщества, мы должны предоставить пользователям возможность полностью удалить свой профиль, если они хотят отменить свое участие в нашем сообществе.

Вот и моя проблема. Допустим, у нас есть 3 зарегистрированных пользователя: Алиса (uid = 1), Боб (uid = 2) и Крис (uid = 3). Теперь Боб хочет удалить свой профиль и прекратить использовать наше сообщество. Если мы удалим профиль Боба из таблицы 'users', то его пропущенный 'uid' создаст пробел, который больше никогда не будет заполнен. На мой взгляд, это огромная трата UID. Я вижу 3 возможных решения здесь:

1) Увеличьте емкость поля 'uid' в нашей таблице с SMALLINT (int (2)) до, например, BIGINT (int (8)) и игнорируйте тот факт, что некоторые из uid будут потрачены впустую.

2) ввести новое поле 'is_deleted', которое будет использоваться для маркировки удаленных профилей (но вместо того, чтобы хранить их в таблице, а не удалять), чтобы повторно использовать их идентификаторы для вновь зарегистрированных пользователей. Таблица будет выглядеть так:

CREATE TABLE `users` (
  `uid` int(2) unsigned NOT NULL auto_increment COMMENT 'User ID',
  `name` varchar(20) NOT NULL,
  `password` varchar(32) NOT NULL COMMENT 'Password is saved as a 32-bytes hash, never in plain text',
  `email` varchar(64) NOT NULL,
  `is_deleted` int(1) unsigned NOT NULL default '0' COMMENT 'If equal to "1" then the profile has been deleted and will be re-used for new registrations',
  `created` int(11) unsigned NOT NULL default '0' COMMENT 'Timestamp of registration',
  `updated` int(11) unsigned NOT NULL default '0' COMMENT 'Timestamp of profile update, e.g. change of email',
  PRIMARY KEY  (`uid`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

3) Напишите скрипт для смещения всех следующих записей пользователя после удаления предыдущей записи. Например. в нашем случае, когда Боб (uid = 2) решает удалить свой профиль, мы заменили бы его запись записью Криса (uid = 3), чтобы uid Криса стал равен 2 и пометил (is_deleted = '1') старая запись Криса как вакантная для новых пользователей. В этом случае мы сохраняем хронологический порядок идентификаторов UID в соответствии с временем регистрации, чтобы у старших пользователей были более низкие UID.

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

Заранее спасибо!

Ответы [ 5 ]

2 голосов
/ 26 мая 2010

определенно не идея движущихся идентификаторов пользователей - это в какой-то момент убьет вас или ваш сервер mysql. Допустим, у вас есть 1 000 000 пользователей, а пользователя 2 удаляют - вам нужно было сдвинуть 999 999 записей на одну ... так просто, как если бы запрос был все еще заблокирован на некоторое время. Также я думаю, что это портит ваше значение auto_increment, которое устанавливается при каждой вставке для каждой таблицы. insert -> AI + 1 -> insert -> AI + 1 -> delete -> AI остается прежним ... если бы вы сместили все идентификаторы, следующее значение auto_increment по-прежнему будет 1 000 001, которое теперь оставит 1 000 000 пустым.

я говорю без знака BIGINT и игнорирую его - потому что, если вы приблизитесь даже к пределу bigint, у вас будет много других проблем;)

1 голос
/ 23 декабря 2010

Я написал простую функцию PHP для «заполнения» пробелов в auto_increment, вызванных запросами «delete», и установки правильного значения «next auto_increment».

function mysql_fix_aigap($table,$column){

$fix_aigap=1;

$query_results=mysql_query("select * from $table");

while($row=mysql_fetch_array($query_results)){

mysql_query("update $table set `$column`='$fix_aigap' where `$column` like {$row[$column]};");

$fix_aigap=$fix_aigap+1;

  }

mysql_query("alter table `$table` AUTO_INCREMENT =$fix_aigap");

}

и позвоните по этому номеру:

mysql_fix_aigap("gapped_table_to_be_fixed","column"); //"users" and "uid" in your case.

(Этот сценарий предполагает, что вы уже подключены к серверу и выбрали базу данных!)

И это был технический ответ.

По моему честному мнению, я бы не рекомендовал назначать "переменную" uid имени пользователя, это не шизофренический способ! (ID = идентичность)

т.

0 голосов
/ 26 мая 2010

Максимальное значение целого без знака равно 4 294 967 295. В настоящее время население Интернета составляет около 1,8 миллиарда человек. Я бы порекомендовал использовать unsigned int для ваших целей и не беспокоиться о пробелах в вашей последовательности.

На философской ноте: Дональд Кнут однажды сказал: «Мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация - корень всего зла».

0 голосов
/ 26 мая 2010

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

Кстати, в MySQL INT(2) 2 задает максимальную ширину дисплея , но не влияет на объем памяти. INT(8) использует то же хранилище, что и INT(2) - используйте BIGINT , как вы предполагаете.

0 голосов
/ 26 мая 2010

Прежде всего; почему вы думаете, что это "пустая трата" uids? Я имею в виду, это просто целое число (или BIGINT), это уже не 70-е годы.

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

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

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