Pull to refresh
46
0
Gleb Deykalo @freebin

User

Send message
по слухам проблема булинга школе стоит довольно остро


У меня двое детей, учились в двух разных школах (по причине «перебрались ближе к дому»), и кажется, что в Российских школах было на порядок больше буллинга (но сужу по себе, дети в школу пошли уже в UK).
Откуда инфа? :)

Всё сильно зависит от строк, имхо. Если в строке два инта — можно сильно не ограничиваться.
Самое главное забыл: отличная статься, можно ей маркер «Tutorial» поставить даже
Есть ещё специфический вариант с полной картой. Нужны два списка: список шардов и карта ID элемента -> ID шарда. При создании новой сущности, шард выбирается по любым удобным правилам (случайно, раунд-робином или по весам — it's up to you). В карту пишется какому шарду принадлежит сущность.

Плюсов два:
— перемещение единичных сущностей упропрощается донельзя
— распределение по шардам контроллируется максимально гибко

Минусы очевидны:
— размер карты равен количеству сущностей
— на каждое чтение сущности нужно прочитать запись из карты

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

Использовала ли Badoo какую-то готовую систему или же это все писалось в компании с нуля?


Писали с нуля, затачивали под свои нужды.

Интересно например, при такой нагрузке — вероятно где-то заранее(на каком этапе?) формируется готовый набор языковых шаблонов, или же какие-то из описанных ситуаций в нем все-же остаются переменными?


Да и да :) Статичные лексемы (не зависящие от внешних данных, например «Здравствуйте, {{user_name}}») генерируются на этапе деплоя кода — почитайте нашу статью про разработку и деплой.

В лексемах, зависящих от числа и использующих склонения остаётся определённая доля динамики (например, «Вы понравились 3 девушкам» = «Вы понравились {{users_num}} {{users_word#Dative}}»)

Также интересно, насколько система переводов в Badoo является самостоятельной и монолитной. Или же наоборот, ее части размазаны по проекту.


Система достаточно монолитна. У нас есть два типа хранения переводов: словари и текст в шаблонах. При этом в шаблонах нет никаких указаний на переводы, а в коде присутствуют только вызовы «дай-ка мне вот эту фразу из словаря». Словари используются как для веб, так и для мобильных приложений. И словари, и шаблоны генерируются на этапе деплоя веба или на этапе сборки приложения.
Как я уже говорил, мы затачиваем свою систему переводов под себя. И поверьте, система переводов Badoo гораздо сложнее, чем простое key-value хранилище. Для геттекста пришлось бы выстраивать «наше АПИ поверх», и не факт что было бы быстрее в разработке и производительнее. Мы выбрали гибкость.

Судя по текущему состоянию голосования, следующая статья будет как раз об этом ;)
Да, я не объяснил, извините :)

Объект-обёртка создаётся для конкретного языка, на котором отображается сайт или генерируется ответ для мобильного приложения. Ну и наши объекты умеют зависеть только от числа (передаётся из кода) и падежа (выбирается переводчиком). То есть в псевдокоде как-то так:

$current_language = 'RU'; //Берём текущий язык
$Localisation = new Localisation($current_language); //Создаём объект "локализация"

$users_num = 5; //Число пользователей
$users_word = 'girl'; //Идентификатор слово, которое будет зависеть от числа и склоняться
$Word = $Localization->getNumDependent($users_word, $users_num); //Получаем тот самый "объект-обёртку"

//Готовим параметры для шаблонизатора
$params = array(
    'users_num' => $users_num,
    'users_word' => $Word,
);

//Выводим "Вы понравились 5 девушкам"
echo Render('Вы понравились {{users_num}} {{users_word#Dative}}', $params);
Спасибо, постараемся.

Тема склонений действительно очень интересна, но текущая реализация нас не очень устраивает из-за того, что разработчик должен обеспечить передачу объекта-контейнера в шаблонизатор. Сейчас мы работаем над тем, чтобы переводчик мог сам, без участия разработчиков, сделать часть лексемы зависимой от числа и/или заменить слово объектом-контейнером. Самый сложный вопрос в таком решении — насколько это ударит по перфомансу.
Как правило для фраз, зависящих от пола, создаётся три варианта: Male, Female и Unknown
Unknown используется не только в тех случаях, когда пол не указан, но и когда речь идёт о пользователях разных полов. Такие лексемы перефразируются максимально нейтрально.
Шаблоны, конечно же, одни на всех. Переводчик видит лексему и переменные в ней, исходный вид "{{users_num}} {{users_word}} liked you" — никаких падежей.

Для каждого языка существует свой собственный набор падежей, и переводчик сам решает, какой падеж применить в его конкретном случае. Фразу ведь можно перефразировать как "{{users_num}} {{users_word}} заценили вас", тогда нужно использовать именительный падеж.

Пусть у нас есть две лексемы:

Вы понравились {{users_num}} {{users_word#Dative}}

{{users_num}} {{users_word#Nominative}} заценили вас

Разработчики обеспечивают, что в шаблонизатор попадёт не конкретное слово «девушка», а объект-обёртка, умеющий получать правильную форму по числу и правильный падеж (указанный в переменной).

Разработчик не знает как переводчик составит фразу, он просто обеспечивает покрытие всех кейсов. А переводчики подготавливают этот универсальный объект примено так:
Согласен, числа прописью делают текст более естественным, но съедают много места, особенно при больших значениях. Давайте считать наш пример промежуточным: не идеальным, но всё же достаточно понятным и гибким (учтены и склонения, и зависимость от числа).
Боюсь такое количество мигрантов будет непросто найти :)
И даже многомесячные ветки были. Если не забывать регулярно в долгие ветки подтягивать свежий мастер — никаких проблем.

Тестируются до билда все ветки на девеле и (не ИЛИ, а именно И) в боевом окружении, в т.н. шотах. Шоты — тема для отдельной статьи, не буду отбирать у авторов этого великого творения право высказаться :)
Значит перестарался упрощать материал. Постараюсь исправиться. Спасибо.
Спасибо. Обязательно дополню.
Описание специально упрощённое. Это достаточно базовые знания о структуре таблиц и индексов, статья писалась для относительных новичков. Мне такой информации в своё время очень не хватало. Именно упрощённой и разжёванной.

Что касается «плоской» и не-совсем-такой-как-в-реальности «индексной таблицы» (каковой, строго говоря, вообще не существует) — это абстракция, упрощающая восприятие. Возможно я зря решил так упростить, но лично мне было бы проще воспринимать именно так.

Речь здесь именно о InnoDB, а там свои правила: dev.mysql.com/doc/refman/5.0/en/innodb-index-types.html И кластерный индекс создаётся всегда.
На закуску то же самое с MyISAM.

CREATE TABLE `t1_isam` (
  `k1` int(10) unsigned NOT NULL DEFAULT '0',
  `k2` int(10) unsigned NOT NULL DEFAULT '0',
  `i1` int(10) unsigned NOT NULL DEFAULT '0',
  `i2` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`k1`,`k2`),
  KEY `secondary_index` (`i1`,`i2`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1


mysql> SELECT * FROM `t1_isam`;
+-----+-----+-----+-----+
| k1  | k2  | i1  | i2  |
+-----+-----+-----+-----+
| 233 | 203 | 315 | 964 |
| 875 | 485 | 801 | 549 |
| 341 |  58 | 267 | 163 |
|  13 | 574 | 833 | 444 |
| 719 | 262 | 152 | 977 |
| 426 | 201 | 726 |  27 |
+-----+-----+-----+-----+
6 rows in set (0.00 sec)


mysql> EXPLAIN SELECT * FROM `t1_isam` WHERE `i1` > 200 AND `i2` < 200\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1_isam
         type: ALL
possible_keys: secondary_index
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 6
        Extra: Using where


Оптимизатор с первой же попытки отказывается использовать ключ, фулскан быстрее. В данном примере MyISAM будет особенно быстр (все поля NOT NULL, все поля фиксированной длины — очень удобно читать).

Ограничиваем выборку полями `i1` и `i2` (они и только они находятся во вторичном ключе в MyISAM).
mysql> EXPLAIN SELECT `i1`, `i2` FROM `t1_isam` WHERE `i1` > 200 AND `i2` < 200\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1_isam
         type: range
possible_keys: secondary_index
          key: secondary_index
      key_len: 4
          ref: NULL
         rows: 5
        Extra: Using where; Using index


Вуаля!
Сам первичный (кластерный) ключ хранится вместе с данными и под абстракцию «индексной таблицы» не подходит. А вот вторичные ключи все имеют скрытую часть в виде полного набора полей ключа первичного.

Пример. Есть таблица t1 (InnoDB) с полями k1, k2, i1, i2. Есть первичный ключ по полям k1, k2 (он и будет кластерным). И есть «обычный» индекс (вторичный ключ) по полям i1, i2.

CREATE TABLE `t1` (
  `k1` int(10) unsigned NOT NULL DEFAULT '0',
  `k2` int(10) unsigned NOT NULL DEFAULT '0',
  `i1` int(10) unsigned NOT NULL DEFAULT '0',
  `i2` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`k1`,`k2`),
  KEY `secondary_index` (`i1`,`i2`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 


Строки в индексной таблице для вторичного ключа будут представлять собой кортежи вида
(`i1`,`i2`, `k1`, `k2`)
где первая пара полей (i) используется для поиска второй (k). Когда получена вторая пара (k, образующая первичный ключ), по ней ищутся строки в таблице данных.

Заполним таблицу случайными данными:
mysql> SELECT * FROM `t1`;
+-----+-----+-----+-----+
| k1  | k2  | i1  | i2  |
+-----+-----+-----+-----+
| 251 | 762 |  60 |  13 |
| 786 | 490 |  92 | 988 |
| 885 | 385 | 272 | 202 |
| 159 | 403 | 537 | 480 |
| 624 | 341 | 830 | 130 |
| 667 | 372 | 856 | 163 |
+-----+-----+-----+-----+
6 rows in set (0.00 sec)


И посмотрим план выполнения простого запроса с фильтрацией:
mysql> EXPLAIN SELECT * FROM `t1` WHERE `i1` > 200 AND `i2` < 200\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: range
possible_keys: secondary_index
          key: secondary_index
      key_len: 4
          ref: NULL
         rows: 4
        Extra: Using where; Using index


Ключ наш оптимизатором запросов признан годным и в плане выполнения используется. Кроме того, Using index в графе Extra означает, что фактического чтения данных с диска не производилось. Все данные брались прямо из индекса. В графе key указан secondary_index, значит все выбранные данные содержатся в нём.

Для доказательства и интереса ради проведём простой эксперимент. Выполним
ALTER TABLE `t1` DROP PRIMARY KEY;
ALTER TABLE `t1` ADD PRIMARY KEY (`k1`);


В результате чего поле k2 выпадает из индексов. План выполнения запроса тут же меняется:
mysql> EXPLAIN SELECT * FROM `t1` WHERE `i1` > 200 AND `i2` < 200\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: ALL
possible_keys: secondary_index
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 6
        Extra: Using where


Обратите внимание на «key: NULL». Поскольку так или иначе придётся читать данные, не входящие в ключ (выбираем-то мы "*"), оптимизатор MySQL принимает решение не использовать ключи вообще, а провести фулскан (type: ALL). Естественно, в Extra больше нет «Using index».

Но стоит нам исключить из выборки поле `k2`, как мы возвращаемся к использованию кластерных ключей:

mysql> EXPLAIN SELECT `k1`, `i1`, `i2` FROM `t1` WHERE `i1` > 200 AND `i2` < 200\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: range
possible_keys: secondary_index
          key: secondary_index
      key_len: 4
          ref: NULL
         rows: 4
        Extra: Using where; Using index

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity