MySQL index usage and LIMIT statement

Рубрика: Development, MySQL, Работа | 5 January 2007, 10:28 | Vadim Voituk

Хочу рассказать об одной достаточно важной особенности SQL-оптимизатора MySQL.
Предположим в таблице почти 390 миллионов (389239897) записей.

Обычный запрос, выбирающий 10 записей за октябрь 2006 г.

EXPLAIN
SELECT *
FROM my_table
WHERE
    c_time>='2006-10-01 00:00:00' AND
    c_time<'2006-11-01 00:00:00'
LIMIT 10;

Обращаем внимаени на значения:
key: i_c_time
rows: 89221170

Такой же запрос, но без указания LIMIT:

EXPLAIN
SELECT *
FROM my_table
WHERE
    c_time>='2006-10-01 00:00:00' AND
    c_time<'2006-11-01 00:00:00';

key: NULL
rows: 389239897

Видно что разница в том, что во втором запросе не используется индекс по полю c_time (key:NULL)
Следовательно происходит полное сканирование таблицы.

Насильно указываем использовать индекс по c_time:

EXPLAIN
SELECT *
FROM my_table FORCE INDEX (i_c_time)
WHERE
  c_time>='2006-10-01 00:00:00' AND
  c_time<'2006-11-01 00:00:00';

Получаем заветные
key: i_c_time
rows: 89221170

Выясняем каким образом LIMIT влияет на использование индексов в запросах.

Указываем в LIMIT число, меньшее чем количество записей в таблице

... LIMIT 389239890

Получаем заветные
key: i_c_time
rows: 89221170

Указываем в LIMIT число равное количеству записей в таблице:
[sql]… LIMIT 389239897[/sql]
Имеем
key: NULL
rows: 389239897

И аналогичный запрос с указанием LIMIT бОльшим чем записей в таблице

... LIMIT 389239899

И аналогичный результат
key: NULL
rows: 389239897
т.е. полное сканирование всей таблицы

Следовательно при указании значения LIMIT >= количества записей,
оптимизатор MySQL выполняет полное сканирования таблицы.

Подбирая параметры, я пришел к том, что такое поведение наблюдается
только в случае если ожидаемое число строк превышает 20% от общего числа строк.

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

За неименением свободного времени я не стал проводить тесты,
указывая в LIMIT диапазоны значений (например LIMIT 20000,30000),
но что-то мне подсказывает что ситуация будет аналогичная.

Find the food

Рубрика: Development | 24 December 2006, 04:17 | juriy

Зверек, который получился в прошлой заметке, был не слишком жизнеспособным: побегав некоторое время вокруг – он погибал от голода. В этой заметке я постараюсь сделать так, чтобы зверь мог найти корм, хотя бы под собственным носом.
Перед тем, как приступить к написанию кода, надо рассмотреть два важных вопроса: как зверь может получить сведения о внешнем мире, и как на основании этих сведений узнать, что в клетке есть источник пищи.
Начнем по порядку.
Общие сведения о мире, которые не меняются на протяжении игры, можно получить, воспользовавшись классом universum.bi.Constants. К таким сведениям относится, к примеру, размер “джунглей” (ширина и высота). Кроме того, в этом классе “живут” константы, которые определяют механику игры: какое минимальное число энергии необходимо, чтобы произвести потомство, сколько энергии необходимо потратить на атаку, сколько на перемещение. Полный список констант можно посмотреть прямо в исходниках класса.
Информацию о том, по каким правилам проводится игра (тип игры: “дуэль”, “блицкриг” или “джунгли”) можно получить из объекта класса UserGameInfo, который передается в метод reinit вашего зверя.
Последний, и самый важный тип данных: это данные о том, как воспринимает себя и окружающий мир ваш зверек непосредственно в момент хода. К примеру: количество энергии зверька, или данные о клетке, на которой он стоит, или список клеток, на которые зверь может перейти за ход. Всю эту информацию можно получить из объекта класса BeingInterface, который передается в метод makeTurn вашего зверя.
Ниже – пример кода, который получает различные сведения о текущем состоянии джунглей.
[java]

public void printInfo() {
  // Сколько у зверя энергии (на момент хода)
  float currentHealth = world.getEnergy(this);
  // У Грокка не самый лучший английский :-)
  System.out.println("Grokk hav da " + currentHealth + " energi");

  // Получаем координаты клетки в которой находится зверь
  int myPositionX = world.getLocation(this).getX();
  int myPositionY = world.getLocation(this).getY();
  System.out.println("I standin in " + myPositionX + ":" + myPositionY);

  // Получаем данные о клетке, в которой мы находимся
  PointInfo pointInfo = world.getPointInfo(this);
  // получаем данные о том, есть ли в клетке еда!
  float foodCount = pointInfo.getCount(this);
  if (foodCount > 0)
    System.out.println("Yumi, yum! Got " + foodCount + " food");
  else
    System.out.println("Oh, no food!");
}
public Event makeTurn(BeingInterface world) {
  this.world = world;
  printInfo();
  return makeRandomMove();
}

[/java]
Таким образом – есть все необходимые инструменты для того, чтобы “научить” зверя “видеть” еду, если она лежит прямо под ногами.
Остановимся немного на том, как “действует” еда в электронных джунглях. Вы уже запускали эмулятор джунглей, поэтому не могли не заметить маленькие “яблочки” разбросанные по полю. Это и есть источники еды. Каждый источник характеризуется тремя величинами: количеством еды, доступным сейчас, максимальным возможным количеством еды и приростом еды за ход. Получить эти значения для каждой клетки можно, используя функции PointInfo.getCount, PointInfo.getMaxCount, PointInfo.getGrowthRate.
Есть два вида источников: “обычные” и “золотые”. “Золотые” отличаются от “обычных” тем, что все три описанных параметра для них существенно выше (и заполучить такой источник, соответственно, намного приятнее).
Все что осталось – запрограммировать зверька таким образом, чтобы он искал “еду”.
Напишем одну простую функцию, которая будет определять: достаточно ли хорош источник для того, чтобы остановиться на нем:
[java]
private boolean isHive(PointInfo pointInfo) {
return (pointInfo.getCount(this) > 0);
}
[/java]
Создадим маленькую функцию, которая будет форировать событие ACTION_EAT:
[java]
private Event doEat() {
return new Event(EventKind.ACTION_EAT, MASS*Constants.K_bite);
}
[/java]
Теперь остается модифицировать функцию makeTurn:
[java]
public Event makeTurn(BeingInterface world) {
this.world = world;

if (isHive(world.getPointInfo(this)))
return doEat();
else
return makeRandomMove();
}
[/java]
Все готово к тестовому запуску. В этот раз эмулятор джунглей лучше запустить так:
java -jar ejungle_distr.jar -altui -debug
В “альтернативном интерфейсе” можно выбрать View->Show Grid, чтобы отображались границы клеток (так легче контролировать, что зверь действует “по плану”), а опция -debug будет выводить в консоль описания всех событий с параметрами.
Ниже – код того, что получилось:
[java]
package ua.net.lab.beings;
import java.util.List;
import universum.bi.Being;
import universum.bi.BeingInterface;
import universum.bi.BeingParams;
import universum.bi.Constants;
import universum.bi.Event;
import universum.bi.EventKind;
import universum.bi.Location;
import universum.bi.PointInfo;
import universum.bi.UserGameInfo;
import universum.util.Util;
public class Grokk implements Being {

private final static float MASS = 10f;
private final static float SPEED = 2f;

private BeingInterface world;

public String getName() {
return “Grokk”;
}
public String getOwnerName() {
return “Juriy”;
}
public BeingParams getParams() {
return new BeingParams(MASS, SPEED);
}
public void reinit(UserGameInfo info) {
}
public Event makeTurn(BeingInterface world) {
this.world = world;

if (isHive(world.getPointInfo(this)))
return doEat();
else
return makeRandomMove();
}

private boolean isHive(PointInfo pointInfo) {
return (pointInfo.getCount(this) > 0);
}

private Event doEat() {
return new Event(EventKind.ACTION_EAT, MASS*Constants.K_bite);
}

public Event stepOneRight() {
Location myLocation = world.getLocation(this);
Location targetLocation = new Location(myLocation.getX() + 1, myLocation.getY());
return new Event(EventKind.ACTION_MOVE_TO, targetLocation);
}
private Event makeRandomMove() {
// Получаем список клеток, в которые можно попасть за ход
List<Location> canReach = world.getReachableLocations(this);

// Выбираем клетку случайным образом и создаем событие – движение
// в выбранную клетку.
return new Event(
EventKind.ACTION_MOVE_TO,
canReach.get(
Util.rnd(canReach.size())));
}
public void processEvent(BeingInterface bi, Event e) {
}
}
[/java]

Let the Grokk Be

Рубрика: Development | 22 December 2006, 13:35 | juriy

По заявкам трудящихся: как сделать простого зверя.

В этой статье описан процесс создания простого зверька для онлайн соревнования искусственных интеллектов: Электро Джунгли. Детальнее про то, что это за проект можно на официальном сайте: http://www.electricjungle.ru или прочитать в моем маленьком обзоре http://voituk.kiev.ua/2006/12/22/elecrojungle-hunters-twilight/

Я использую Eclipse, поэтому я буду описывать весь “процесс” именно на примере этой IDE.
Итак, для начала необходимо подготовить среду разработки.

1. Создаем новый Java проект.
2. Добавляем в Build Path библиотеку, которую скачали с сайта (для тех, кто еще не скачал – можно взять тут: http://www.electricjungle.ru:8080/main.jsp?page=sdk).
3. Создаем новый package. Создаем в этом package новый класс, который будет описывать логику поведения зверя. В моем примере я буду использовать пакет ua.net.lab.beings, а класс будет называться Grokk. Класс должен реализовывать интерфейс universum.bi.Being.
4. После создания и автоматической генерации заглушек для абстрактных классов код выглядит так:

[java]

package ua.net.lab.beings;

import universum.bi.Being;
import universum.bi.BeingInterface;
import universum.bi.BeingParams;
import universum.bi.Event;
import universum.bi.UserGameInfo;

public class Grokk implements Being {

  public String getName() {
    return null;
  }

  public String getOwnerName() {
    return null;
  }

  public BeingParams getParams() {
    return null;
  }

  public void reinit(UserGameInfo info) {
  }

  public Event makeTurn(BeingInterface world) {
    return null;
  }

  public void processEvent(BeingInterface bi, Event e) {}
}

[/java]

Теперь нужно заполнить заглушки кодом. Начнем с самого простого. Функции getName() и getOwnerName() возвращают, соответственно имя зверюшки и имя создателя.
[java]

public String getName() {
  return "Grokk";
}

public String getOwnerName() {
  return "Juriy";
}

[/java]

Дальше идет функция getParams() – она определяет начальные параметры первого существа (параметры следующих поколений могут быть другими): его массу и скорость. Эти параметры лучше сразу вынести в константы: их придется подбирать экспериментально. Поскольку менять легче в одном месте, то так и поступим:
[java]

private final static float MASS = 10f;
private final static float SPEED = 2f;

....

public BeingParams getParams() {
  return new BeingParams(MASS, SPEED);
}

[/java]

Функция reinit(UserGameInfo info) – вызывается при старте. Из нее зверек может узнать базовые сведения про мир. Здесь немного отвлечемся от кода и снова глянем на правила: есть на сайте описано 3 типа игры:

SINGLE – твоя зверюга – одна в джунглях. Цель – получить за ограниченное количество ходов максимальную массу популяции.
DUEL – думаю, без комментариев – в джунглях 2 вида. Победит тот, у кого после определенного количества ходов – максимальная масса популяции.
JUNGLE – то же самое, что и DUEL, только видов может быть до 8.

В файле с описанием констант: universum.bi.GameKind есть описание еще для двух типов игр: PEACE_DUEL и DEBUG. Поскольку комментариев про эти типы игр нет, то я оставляю читателям трактовать значения.

Вернемся к коду. Поскольку существо не настолько умное, чтобы его поведение менялось в зависимости от типа игры, то функцию reinit(UserGameInfo info) оставим пустой. В более продвинутом зверьке в этой функции можно инициализировать static переменные. Функция вызывается один раз, до установки первого зверя на поле.

Дальше – самое интересное. Функция makeTurn(BeingInterface world) – сердце (вернее, мозг) вашего зверя. Тут зверь решает, куда ему пойти.
Параметр, который передается в функцию, позволяет зверю получить данные о мире, который его окружает. Поскольку для принятия решений о действиях эта переменная будет нужна постоянно, лучше ее с самого начала сделать полем класса, и обновлять каждый раз при вызове makeTurn(BeingInterface world).

[java]

private BeingInterface world;
...
public Event makeTurn(BeingInterface world) {
  this.world = world;
  return null;
}

[/java]

Пускай зверек двигается случайным образом в любую доступную клетку. Для этого я создал отдельную функцию – makeRandomMove(), которая возвращает Event – решение зверька о том, что делать дальше.
[java]

private Event makeRandomMove() {
  // Получаем список клеток, в которые можно попасть за ход
  List<Location> canReach = world.getReachableLocations(this);

  // Выбираем клетку случайным образом и создаем событие - движение
  // в выбранную клетку.
  return new Event(
      EventKind.ACTION_MOVE_TO,
      canReach.get(Util.rnd(canReach.size())));
}[/java]

Обратите внимание на Util.rnd(canReach.size()). Авторы не рекомендуют пользоваться java.util.Random для выбора случайных чисел. Util.rnd() - тот же Random только в профиль. Все чем он отличается: в режиме отладки он генерирует одинаковые последовательности случайных чисел.

Теперь остается поменять makeTurn(BeingInterface world) так чтобы зверь двигался в случайном направлении каждый ход.

[java]
public Event makeTurn(BeingInterface world) {
  this.world = world;
  return makeRandomMove();
}

[/java]

Все что осталось сделать - запаковать зверя в jar и не забыть добавить в MANIFEST.MF строку

Main-Class: ua.net.lab.beings.Grokk

Теперь отпускаем зверя на волю: запускаем эмулятор джунглей:

java -jar ejungle_distr.jar

в GUI добавляем в джунгли нового зверя: Add Being -> Add from jar.

Зверек немного побегает и сдохнет от голода: кушать он не умеет. Но научим мы его позже, сейчас немного поработать надо :)

Вот код того что получилось:
[java]

package ua.net.lab.beings;

import java.util.List;

import universum.bi.Being;
import universum.bi.BeingInterface;
import universum.bi.BeingParams;
import universum.bi.Event;
import universum.bi.EventKind;
import universum.bi.Location;
import universum.bi.UserGameInfo;
import universum.util.Util;

public class Grokk implements Being {
  private final static float MASS = 10f;
  private final static float SPEED = 2f;

  private BeingInterface world;

  public String getName() {
    return "Grokk";
  }

  public String getOwnerName() {
    return "Juriy";
  }

  public BeingParams getParams() {
    return new BeingParams(MASS, SPEED);
  }

  public void reinit(UserGameInfo info) {}

  public Event makeTurn(BeingInterface world) {
    this.world = world;
    return makeRandomMove();
  }

  private Event makeRandomMove() {
    // Получаем список клеток, в которые можно попасть за ход
    List<Location> canReach = world.getReachableLocations(this);

    // Выбираем клетку случайным образом и создаем событие - движение
     // в выбранную клетку.
    return new Event(
      EventKind.ACTION_MOVE_TO,
      canReach.get(Util.rnd(canReach.size())));
  }
  public void processEvent(BeingInterface bi, Event e) {}
}

[/java]

Elecrojungle: hunters twilight

Рубрика: Development | 22 December 2006, 12:07 | juriy

Уж не знаю, как это так выходит, но Вадим обо всем новом и интересном узнает раньше. Только я найду что-то стоящее внимания – кидаю ему ссылку, а он мне в ответ что-то вроде “Тю, ты только узнал, так это уже давно…”. Этот раз исключением не стал, только ссылку кинум не я ему, а он мне. Ссылка эта – http://www.electricjungle.ru/
Суть проекта – довольно интересна. Есть виртуальные джунгли – простая сетка, замкнутая по краям. Есть SDK, который позволяет написать зверька, который будет счастливо (или не очень, все зависит от рук создателя) жить в этих джунглях.
Жить в джунгях не просто, надо бороться за выживание с другими такими же умниками, как ты. Цель – сделать так, чтобы твоя популяция была самой большой после определенного количества ходов.
Правила достаточно просты – их можно посмотреть прямо на сайте. Тут я вкратце коснусь основ создания своего личного зверька.
Зверек характеризуется двумя величинами: размер и скорость. Кроме того у каждого индивида есть энергия, которую он может тратить на действия. Максимальное количество энергии равно массе существа.
Что может делать зверек:

1. Можно стоять и перекуривать (не делать ничего, пропустить ход). Энергии не требует.
2. Можно двигаться. Количество клеток, на которые зверь может перейти за ход – определяется его скоростью. Энергия, которая тратится на это действие прямо пропорционально массе и скорости существа (да, да, можно сделать слона, который будет догонять гепардов, но такая туша должна точно знать, где себя прокормить).
3. Можно есть. Понятно, что энергии не требует. Слопать за раз можно не больше чем 10% от максимальной энергии.
4. Можно атаковать: тратится около 1% энергии за атаку (точное значение нужно глянуть на сайте). Ну и повреждения, которые наносит зверек – пропорциональны массе.
5. Можно отдавать энергию другому существу (“лечить”, так и представляю клирика-носорога!).
6. Можно размножаться. Тут самое интересное. Во-первых, существа размножаются делением (партнер не нужен). Да и сам процесс интересен: чтобы разделиться нужно иметь минимум 80% энергии. 20% тратится на процесс. Половина того что осталось – переходит новому зверьку. Очень классная идея! При рождении новому зверьку можно передать “генокод” – произвольный объект, который затем можно использовать для определения поведение зверька! Кроме того, звери могут понемногу “эволюционировать”. За одно поколение можно изменить на 20% массу и скорость новой особи.

Разработка.
Разработка своего маленького кибернетического чуда – не составляет особого труда. Основная сложность на моем пути заключалась в том, что документация… нет она не плохая ее просто нет. Все чем вы располагаете вначале – это SDK с исходниками и один простой зверек, на которого можно смотреть.
Я опишу процесс создания и запуска простого зверька “с высоты птичьего полета”. Может, если тема будет развиваться напишу небольшой “kick-off”, который опишет процесс “по шагам”. Кстати, если будет интересно – пишите.

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

public Event makeTurn(BeingInterface world)

В ней зверек решает, что ему делать, основываясь на данных, полученных от внешнего мира и делает ход.
После этого – необходимо запаковать зверя в .jar и в манифесте указать в параметре Main-Class – класс зверя (мне кажется, это неправильный подход, ведь этот атрибут для другого предназначен). Теперь можно запустить софтинку, загруженную с сайта, и добавить зверя в джунгли. Все, можно наслаждаться наблюдением за ростом вашего чёртика!

J2ME 101 on ibm.com

Рубрика: Development | 19 December 2006, 09:38 | Vadim Voituk

В рамках колонки “J2ME 101″ на IBM DeveloperWorks опубликовано 2 русскоязычных статьи о высокоуровневых и низкоуровневых пользовательских интерфейсах в J2ME-приложениях.

Для новичков очень даже неплохо.

Updated: Добавлена статья “Доступ к мобильной базе данных”.
 

Tomcat resource usage monitoring using jConsole

Рубрика: Development, Java | 7 December 2006, 11:21 | Vadim Voituk

Ни для кого не секрет что серверные Java-приложения требуют под себя относительно много оперативной памяти, а OutOfMemoryException достаточно частое явление в J2EE среде.
Потому мониторинг загруженности сервера является достаточно необходимой задачей.

Самым примитивным методом мониторинга является *nix команда top.
Вот только полезной информации она дает мало, да и мониторит загруженность всей программно-апаратной системы.
Меня же чаще всего интерисует сколько ресурсов “отьедает” мое приложение в паре с сервлет-контейнером.
Можно, конечно, смотреть сколько ресурсов отедают всуме все java-процессы, но это скорее костыль, чем решение.
К моему огромному счастью в Java 5 придумали JMX API а к нему и jConsole.

С этого момента жизнь становится “проще и уютней”. Запускаем Tomcat таким образом:
$export JAVA_OPTS='-Dcom.sun.management.jmxremote.port=8004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false'
$/usr/local/tomcat/bin/catalina.sh start

далее на рабочей машине запускаем jconsole и указываем удаленное подключение на сервер по укананному (8004-му) порту.
В результате видим подобную картину:

jconsole

Более подробно можно прочитать тут.

P.S. Достаточно интересно наблюдать работу Garbage Collector на Heap Memory Usage Monitor – получается такая себе “пила”.

Internet Explorer id & name bug?

Рубрика: Development, JavaScript/Ajax | 4 December 2006, 15:21 | Vadim Voituk

Такую интересную ошибку нашел в JavaScript-обработчике InternetExplorer.

Предположим в документе есть элемент с name=”imp1″ и за ним идет элемент с id=”imp1″.
При попытке получить второй обьект по с помощью getElementById(“imp1″) InternetExplorer возвращает первый элемент (тобишь тот у которого name=”imp1″).

Пример:

[xml]
<input type="text" name="imp1" value="Неправильно! id=imp2, name=imp1" id="imp2"  />
<input type="text" name="blah" value="Правильно! id=imp1" id="imp1"  />
<input type="button" onClick="alert(document.getElementById(str).value);" value="Тест id=imp1" />
[/xml]

Если открыть этот документ в FireFox, то при нажатии на кнопку получим текст “Правильно! id=imp1″
Если же в Internet Explorer – то “Неправильно! id=imp2, name=imp1″

Причина скорее всего кроется в том, что IE использует один хэш document.all для хранения ссылок по id и по name, а getElementById() использует именно этот хэш.

Developers will not die!

Рубрика: Development, Работа | 17 November 2006, 13:15 | Vadim Voituk

Согласно исследованиям американского журнал Money Magazine лидером среди самых востребованых специальностей следующего десятилетия являются программисты.

Людей, близких к сфере ИТ, подобные заявления не удивляют. Но для тех кто не понимает чем же все-таки занимается “этот небритый мохнатый дядька за компьютером” подобные публикации могут вызвать массу негодования смешанного с обидой и удивлением.

Радует что количество технически-образованных людей с каждым днем увеличивается и страх перед компьютером становится историческим архаизмом.
Следовательно все больше и больше задач будет решаться на ПК – а значит программисты не вымрут как вид :)

Apache/PHP VS Tomcat/Groovy V.2

Рубрика: Development, Groovy, Java | 15 November 2006, 14:15 | Vadim Voituk

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

Дотаточно здравой мыслью показалось построение пула соединений к базе данных.
Создал DataSource и переписал Groovelet для использования соединения из пула.
Провел замеры производительности и ВУАЛЯ!
Значение Request Per Second показало числа от 480 до 650, при значениях для Apache+PHP от 280 до 350.

Следующим шагом планирую реализовать кеширование данных в Groovelet-e с помошью JCS.

After JUG

Рубрика: Development, Groovy, Java | 13 November 2006, 15:53 | Vadim Voituk

В субботу, 11 ноября, мне представилась возможность посетить встречу Киевского “отделения” Java Users Group.
Спонсором мероприятия выступила компания Global Logic (если мне мне изменяет память, это та что была Bonus Technologies, что в свою очередь была частью Miratech).

Запланировано было 3 доклада:

1. JetBrains TeamCity , Дмитрий Жемеров
2. Web development in Python, Максим Ищенко
3. Intellij IDEA 6.0: new features, Дмитрий Жемеров

Первый – посвящен интересной Continuous Integration Tool от JetBrains – TeamCity.
Доклад был построен достаточно грамотно и интересно. Вопросов у слушателей почти не вызвал.
Второй – был призван донести до Java – сообщества существование такого языка как Python, а также его возможности для WEB-разработки.
Очень жаль, что ничего небыло сказано о Groovy или Jyton.
Третий доклад – о нововведениях в Intellij IDEA 6.0 – мне дослушать не удалось, да и ценность его для меня, использующего Eclipse, весьма сомнительна.

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

Страница 15 из 17« Первая...34567891011121314151617