2014-06-25

MSSQL: псевдопартиционирование

Допустим, у нас есть (а у нас есть) таблица огромных размеров, с огромными индексами и вообще всё у неё огромное. Скажем, это записи из твиттера. Из этой таблицы часто делаются выборки по полю DATETIME. И, хотя на этом поле есть индекс, и несомненно есть Primary Key (вобщем все как надо), доступ к данным осуществляется дольше, чем хотелось бы: для использования индекс загружается в оперативную память, а он, как вы помните, огромный.

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

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

CREATE VIEW table AS
SELECT * FROM table1 WHERE created_at BETWEEN '2014-02-01 00:00:00.000' AND '2014-03-01 00:00:00.000'
UNION ALL
SELECT * FROM table2 WHERE created_at BETWEEN '2014-03-01 00:00:00.000' AND '2014-04-01 00:00:00.000'

Фокус в том, что если запросить данные с указанием условия, присутствующего во VIEW, таблица, не удовлетворяющая этому условию затронута не будет вовсе. Например:

SELECT * FROM table WHERE created_at > '2014-03-01 00:00:00.000'

заденет только вторую таблицу, соответственно поднимет только ее индекс. Как-то так.

2014-06-20

MongoDB: шардинг по быстрому

Шардинг, как известно, позволяет раскидать данные по нескольким серверам БД, распределяя их согласно некоторым условиям. В MongoDB это бесплатно, так сказать all inclusive, к тому же невероятно просто.

Запустим несколько шардов, например на разных портах (или серверах, если есть). Я это делаю в нескольких терминалах, чтобы видеть, что происходит. Но можно добавить ключ --fork для отвязки от терминала.

$ mongod --dbpath db1 --port 27001 --shardsvr
$ mongod --dbpath db2 --port 27002 --shardsvr
$ mongod --dbpath db3 --port 27003 --shardsvr

Запустим шард-сервер и назначим ему один из шардов (на порту 27001) в помощь - там он будет хранить информацию о том, куда что записал:

$ mongos --configdb 127.0.0.1:27001

Цепляемся к шард-серверу, подключаем шарды, указываем что шардить и по какому ключу:

$ mongo
> use admin
> db.runCommand({addshard: "127.0.0.1:27002"})
> db.runCommand({addshard: "127.0.0.1:27003"})
> db.runCommand({enablesharding: "social"})
> db.runCommand({shardcollection: "social.tweets", key: {id:1}})

MongoDB: репликация по быстрому

Вдохновленный этой краткой заметкой решил сделать себе мемо в блоге, дополнив некоторыми замечаниями. Итак, репликация в несколько строк:
Создаем каталоги для данных:

mkdir -p db1 db2 db3

В терминалах запустим несколько реплик в одном наборе "myset", на своих портах и со своими каталогами для данных:

$ mongod --port 27001 --dbpath db1 --replSet myset
$ mongod --port 27002 --dbpath db2 --replSet myset
$ mongod --port 27003 --dbpath db3 --replSet myset

Подключаемся к одной из реплик, делаем ее "мастером", подключаем к ней остальные реплики и проверяем:

$ mongo --port 27001
> rs.initiate()
> rs.add("myhost:27002")
> rs.add("myhost:27003")
> use test
> db.test.insert({id:1})
> db.test.find()

где "myhost" - имя хоста, которое можно увидеть в выводе команды rs.status()

Примечательно и прекрасно, что реплики могут находиться на разных машинах, увеличивая бутылочное горлышко I/O. В своих экспериментах я размещал реплики на PC с Windows 7 на борту, на Macbook и на Ubuntu в VirtualBox.

Важно, чтобы машина с репликой могла однозначно интерпретировать хост "мастера" в IP. На макбуке мне для этого пришлось добавить запись в /etc/hosts:

myhost 192.168.10.130

2014-03-31

Запуск скрипта при старте и выключении CentOS Linux

Задача: в CentOS запускать некий скрипт "test.py" при старте и выключении компьютера от имени некоего пользователя. Для ее решения напишем init-скрипт, например "status_sender" и поместим его в каталог /etc/rc.d/init.d/

Содержимое init-скрипта:


#!/bin/bash

# chkconfig: 35 99 01
# description: Send system status
# processname: status_sender

. /etc/rc.d/init.d/functions

username="username"
lock_file=/var/lock/subsys/status_sender
log_datetime=`date -u +"%F %T"`

start(){
    touch "$lock_file"    
    daemon --user=$username /usr/local/bin/python2.7 "/home/$username/test.py" "Service started at $log_datetime UTC"  &>/dev/null & 
}

stop(){
    rm -f "$lock_file"
    daemon --user=$username /usr/local/bin/python2.7 "/home/$username/test.py" "Service stopped at $log_datetime UTC" &>/dev/null &
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    *)
        echo $"Usage: $0 {start|stop}"
        exit 2
esac
exit 0

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


chkconfig --add status_sender

Проверить результат можно командой


chkconfig --list status_sender

Настройки того, куда и с какими именами будут создаваться симлинки, задаются в строке "chkconfig: 35 99 01". Это означает, что скрипт будет актуален для запуска системы с уровнем 3 и 5, причем старт осуществлять после загрузки всех сервисов (99), а останавливать в первую очередь (01).

Запуск от имени пользователя осуществляется с помощью конструкции


daemon --user=$username

где "$username" - имя пользователя в системе, а "&>/dev/null" предотвращает вывод скрипта в консоль. Команда "daemon" и другие служебные функции находятся в файле "/etc/rc.d/init.d/functions", поэтому его мы импортируем в самом начале.

"lock_file" - служебный файл, который позволяет системе определять, запущен ли сервис и нужно ли его останавливать, запуская скрипт с параметром "stop" при выключении или презагрузке. Если его не создавать, то будет осуществляться только запуск скрипта, а если не удалять - то только остановка. Для системных сервисов такой файл создается автоматически, для пользовательских же его необходимо создать самостоятельно.

"log_datetime" - это строка даты и времени в UTC для передачи python-скрипту.

2014-03-05

Загрузка файла на Amazon EC2 с помощью Python

Есть у меня задачка, с определенной периодичностью заливать через FTP на имеющийся хостинг Amazon EC2 несколько файлов. Некоторое время я делал это тупо через FTP-клиент. Открыл, соединился, нашел нужную папку, закинул файл, закрыл клиент... Вобщем, как обычно.
Но настоящий программист - ленивый программист. Он всегда ищет, как бы автоматизировать рутинные процессы. В решении этой задачи я использую Python.
В составе поставки есть модуль для работы с FTP: ftplib. Однако в нем не нашлось методов для работы с SFTP, использующим для авторизации файл ключа *.pem. Поэтому я использовал pysftp
Использование выглядит простым, как штанга. Пример все пояснит.

srv = pysftp.Connection(host='sitename.org', username='username', private_key='/path/to/file.pem')
srv.execute('mkdir /server/path')
srv.put('/path/to/local/file.html', '/server/path/file.html')
srv.close()

А теперь начнем делать чудеса, доступные пользователям Mac. Создаем каталог, на который навешиваем действие, созданное в Automator. Событие добавления нового файла будет вызывать наш скрипт, передавая ему путь к файлу. После загрузки будет выведено сообщение во всплывающем окне.