powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Партиционирование (нудно, сплошные сорцы :-)
18 сообщений из 18, страница 1 из 1
Партиционирование (нудно, сплошные сорцы :-)
    #34385896
glebofff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Для начала, пара полезных функций:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
-- функция для копирования (создания таких же, как и у src-таблицы) ограничений - constraints
CREATE OR REPLACE FUNCTION copy_constraints(srcoid oid, dstoid oid)
  RETURNS integer AS
$BODY$
declare 
  i int4 :=  0 ;
  constrs record;
  srctable text;
  dsttable text;
begin
  srctable = srcoid::regclass;
  dsttable = dstoid::regclass;
  for constrs in 
  select conname as name, pg_get_constraintdef(oid) as definition 
  from pg_constraint where conrelid = srcoid loop
    begin
    execute 'alter table ' || dsttable 
      || ' add constraint ' 
      || replace(replace(constrs.name, srctable, dsttable),'.','_') 
      || ' ' || constrs.definition;
    i = i +  1 ;
    exception
      when duplicate_table then
    end;
  end loop;
  return i;
exception when undefined_table then
  return null;
end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

-- перегрузим
CREATE OR REPLACE FUNCTION copy_constraints(src text, dst text)
  RETURNS integer AS
$BODY$
begin
  return copy_constraints(src::regclass::oid, dst::regclass::oid);
end;
$BODY$
LANGUAGE 'plpgsql' IMMUTABLE;

-- функция для копирования индексов
CREATE OR REPLACE FUNCTION copy_indexes(srcoid oid, dstoid oid)
  RETURNS integer AS
$BODY$
declare 
  i int4 :=  0 ;
  indexes record;
  srctable text;
  dsttable text;
  script text;
begin
  srctable = srcoid::regclass;
  dsttable = dstoid::regclass;
  for indexes in 
  select c.relname as name, pg_get_indexdef(idx.indexrelid) as definition 
  from pg_index idx, pg_class c where idx.indrelid = srcoid and c.oid = idx.indexrelid loop
    script = replace (indexes.definition, ' INDEX ' 
      || indexes.name, ' INDEX ' 
      || replace(replace(indexes.name, srctable, dsttable),'.','_'));
    script = replace (script, ' ON ' || srctable, ' ON ' || dsttable);
    begin
      execute script;
      i = i +  1 ;
    exception
      when duplicate_table then 
    end;
  end loop;
  return i;
exception when undefined_table then
  return null;
end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

-- перегрузим
CREATE OR REPLACE FUNCTION copy_indexes(src text, dst text)
  RETURNS integer AS
$BODY$
begin
  return copy_indexes(src::regclass::oid, dst::regclass::oid);
end;
$BODY$
LANGUAGE 'plpgsql' IMMUTABLE;

-- функция для копирования триггеров (для партиционирования, они, собственно... хотя... ну мало ли, в общем :))
CREATE OR REPLACE FUNCTION copy_triggers(srcoid oid, dstoid oid)
  RETURNS integer AS
$BODY$
declare 
  i int4 :=  0 ;
  triggers record;
  srctable text;
  dsttable text;
  script text = '';
begin
  srctable = srcoid::regclass;
  dsttable = dstoid::regclass; 
  for triggers in 
   select tgname as name, pg_get_triggerdef(oid) as definition 
   from pg_trigger where tgrelid = srcoid loop
    script = 
    replace (triggers.definition, ' TRIGGER ' 
      || triggers.name, ' TRIGGER ' 
      || replace(replace(triggers.name, srctable, dsttable),'.','_'));
    script = replace (script, ' ON ' || srctable, ' ON ' || dsttable);
    begin
      execute script;
      i = i +  1 ;
    exception
      when duplicate_table then 
    end;
  end loop;
  return i;
exception when undefined_table then
  return null;
end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

-- и тут перегрузим
CREATE OR REPLACE FUNCTION copy_triggers(src text, dst text)
  RETURNS integer AS
$BODY$
begin
  return copy_triggers(src::regclass::oid, dst::regclass::oid);
end;
$BODY$
LANGUAGE 'plpgsql' IMMUTABLE;
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34385910
glebofff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Посмотрим пример отсюда .
postgresql.org
For example, suppose we are constructing a database for a large ice cream company. The company measures peak temperatures every day as well as ice cream sales in each region. Conceptually, we want a table like this:
Код: plaintext
1.
2.
3.
4.
5.
6.
CREATE TABLE measurement (
    city_id         int not null,
    logdate         date not null,
    peaktemp        int,
    unitsales       int
);

Вручную создавать партиции, индексы и правила - лень. Поэтому создадим их для measurement:

Код: plaintext
1.
2.
3.
4.
5.
 CREATE INDEX measurement_logdate ON measurement (logdate);
 CREATE INDEX measurement_peaktemp ON measurement (peaktemp);
 CREATE INDEX measurement_city_id ON measurement (city_id);
 -- забегая вперёд :-)
 COMMENT ON COLUMN measurement.logdate IS 'for partitioning';
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34385921
glebofff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А потом напишем функцию, которая создаёт партиции и правила:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
CREATE OR REPLACE FUNCTION partition_month (parentoid oid, scheme character varying, dateval date)
  RETURNS text AS
$BODY$
declare 
  parent text := parentoid::regclass;
  suffix text := to_char (dateval, '_YYYY_MM'); 
  child  text := scheme || (select relname from pg_class where oid  = parentoid) || suffix;
  check_beg varchar;
  check_end varchar;
  check_condition varchar;
  check_field     varchar := null;
  tmp record;
  script text := '';
  i int :=  0 ;
  j int :=  0 ;
begin
  -- таблица уже существует
  perform child::regclass;
  return child;
exception 
  when undefined_table then
    -- интервалы для constraint check
    check_beg = to_char (dateval,'YYYY-MM-01');
    check_end = to_char (check_beg::date + interval '1 month' - interval '1 day','YYYY-MM-DD');

    -- пробегаемся по полям родительской таблицы, составляем values для insert rule
    j = (select count(*) from pg_attribute where attrelid = parentoid  and attnum > 0 );
    for tmp in 
    select attname from pg_attribute where attrelid = parentoid and attnum > 0  order by attnum loop
      i = i +  1 ;
      script = script || 'NEW.' || tmp.attname || case i when j then '' else ',' end;
      -- заодно выясняем поле для партиционирования и составления условия проверки
      if (col_description (parentoid, i) ~* 'partition') and (check_field is null) 
        then check_field = tmp.attname; end if;
    end loop;
    script = script || ')';

    -- условие для проверки
    check_condition =      
    '( 
       ' || check_field || ' >= ' || quote_literal (check_beg) || ' and 
       ' || check_field || ' <= ' || quote_literal (check_end) || ' 
     )';

    -- создаём child-таблицу
    execute 
    'create table ' || child || '
    (
     constraint partition' || suffix || ' check ' 
      || check_condition || '
    ) 
    inherits (' || parent || ')'; 

    -- создаём правило для parent-таблицы
    execute 
    'create rule route' || suffix || ' as '
      || ' on insert to ' || parent || ' where '
      || check_condition
      || ' do instead insert into ' || child
      || ' values (' || script;

    -- копируем ключи и индексы
    perform copy_constraints(parent, child);
    perform copy_indexes(parent, child);
  return child;
end;
$BODY$
LANGUAGE 'plpgsql';
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34385929
glebofff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Теперь, чтобы создать партицию, можно вызвать функцию partition_month:

Код: plaintext
1.
2.
  create schema partitions;
  select partition_month('measurement'::regclass::oid, 'partitions.', current_date);

Можно и триггер:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
CREATE OR REPLACE FUNCTION measurement_router_trig()
  RETURNS "trigger" AS
$BODY$
begin 
  perform partition_month (TG_RELID, 'partitions.', NEW.logdate);
  return NEW;
end;
$BODY$
LANGUAGE 'plpgsql' immutable;

CREATE TRIGGER measurement_router BEFORE INSERT ON measurement 
  FOR EACH ROW EXECUTE PROCEDURE measurement_router_trig();

Тут слабое место - NEW.logdate. Можно написать функцию, составляющую триггер для oid. Или передавать просто - NEW - в функцию, вызывающую в свою очередь partition_month (overloading), или передавать anyelement - и у нас уже полиморфизм. :-)

Проверяем триггер:
Код: plaintext
1.
2.
3.
insert into measurement
 select round(random()* 1000 ), (y||'-'|| m||'-'||d)::date, round(random()* 1000 ), round(random()* 1000 ) 
 from generate_series( 2000 , 2005 ) y, generate_series( 1 , 12 ) m, generate_series ( 1 ,  15 ) d;
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34392911
glebofff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В общем, если вдруг кому интересно, попробую изложить дальше мысли по поводу автоматического создания партиций.

Вот такое абстрактное время (в секундах) выполнения вставки семи тысяч строк в measurements:
Код: plaintext
1.
2.
3.
4.
                  первая вставка / вторая (когда таблицы создавать уже не нужно)
rule:             10             / 6.5 (5.5)
триггер, plpgsql: 8              / 2.5
триггер, си:      7              / 1.6

Видно, что в отличие от (свежесозданного) набора правил, триггер просто рулит. :-)
Как оно работает:

plpgsql:
1. выясняется имя нужной партиции
2. генерируется строка для values (...)
3. execute 'insert...'

си:
1. SPI_blah используется для того, чтобы выяснить oid нужной партиции
2. по oid открывается соответствующая реляция,
3. вставляется tuple (heap_insert),
4. генерируются индексы (FormIndexDatum, index_insert)

В сишном триггере (он сырой и вообще for fun), собственно, для партиции не проверяются constraint checks, а также не поддерживаются partial и ещё бог знает какие индексы. Причём, тут есть повод задуматься. Например, вообще не строить индексы во время вставки (для доступа к новым данным нужно будет отключать enable_indexscan и enable_bitmapscan), а перестраивать их потом, после вставки огромного количества строк.

Переработанные исходники (в том числе и сишного триггера): здесь
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34434704
darkpiper
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Что то у меня не получилось !!
При проверке все данные оказались в одной таблице
После удаления данных и повторном Insert все встало на свои места....
Я в вашем исходнике запутался :(
Я только начинающий и непонимаю в чем отличие RULE от TRIGGER
(вернее я не понимаю что на каком этапе срабатывает)

PS Windows XP / PostgreSQL 8.2.3
PPS Все настройки по умолчанию
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34653797
sybasesql
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый день.

Тема более чем актуальна. Решение довольно элегантно и компактно. Для меня лично решение используя С триггер более приемлемо.
К сожалению не смог оценить С-триггер, т.к. не работает указанная Вами ссылка. Если есть возможность, выложите еще раз.

Спасибо.
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34654862
MBG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
MBG
Гость
Сегодня что, первое апреля? Это сообщение я уже когда-то видел, опять над новичками издеваетесь.

Выкопал свои функции создания разделенных таблиц, см.

http://postgrestips.blogspot.com/2007/06/partitial-table.html

P.S. Вот после таких шуточек от постгреса шарахаются, как черт от ладана... Идите к мускульщикам, у них все равно база падает постоянно, авось не побьют.
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34654912
glebofff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MBGСегодня что, первое апреля? Это сообщение я уже когда-то видел, опять над новичками издеваетесь.

Выкопал свои функции создания разделенных таблиц, см.

http://postgrestips.blogspot.com/2007/06/partitial-table.html

P.S. Вот после таких шуточек от постгреса шарахаются, как черт от ладана... Идите к мускульщикам, у них все равно база падает постоянно, авось не побьют.

Угу. Первое апреля. Объясняю: была необходимость создания универсального решения. С чем, собственно, и справился. Здесь хотел поднять тему для обсуждения, но, видимо, неправильно начал - с публикации невнятных исходников. :-) Моя вина.

Теперь по поводу статьи, которая по ссылке.

Код: plaintext
Теорию Вы можете прочитать здесь, а ниже мы рассмотрим, как на практике использовать данный метод.
В этом-то и вся закавыка. Неинтересен мне данный метод.

1. Пытался ли автор автоматизировать "маршрутизацию" данных при вставке оных в родительскую таблицу? Или запрет диапазонов - это решение?

"Теперь данные, для которых нет подтаблицы будут игнорироваться с выдачей..." - а не лучше ли тут же и создать такую "подтаблицу"? :-) Мне лично такой вариант симпатичнее.

2. Были ли попытки посчитать время, затраченное на вставку данных при росте набора вот этих вот
Код: plaintext
1.
CREATE OR REPLACE RULE out_insert_out_yy06mm01 AS
ON INSERT TO data."out"
правил и соответственно кол-ва дочерних таблиц?

Теперь попробую объяснить, чего же я всё-таки добивался.

1. Создания дочерних таблиц (или like-таблиц) с индексами, триггерами, проверками, правилами, и т.д., как у предка. Вроде бы в 8.3 это будет, по крайней мере индексы.

2. "Умной" и беззаботной загрузки большого (или небольшого) объёма данных в родительскую таблицу, при этом не заботясь о ручном создании дочерних.

Представим себе (пример с потолка) средненькую такую табличку, допустим, с бухгалтерскими проводками (или с телефонными звонками) средненькой такой фирмы за 3-летний период. Которую вдруг решили "партиционировать" по месяцам. 36 дочерних таблиц - это уже огогоськи. Тут
Код: plaintext
1.
2.
  foreach year { 06   07 } {
    foreach month { 01   02   03   04   05   06   07   08   09   10   11   12 }
foreach year надо будет усиленно модифицировать. :-) А если 100 таблиц?

При этом с "do instead", я уверяю, всё будет грустнее и грустнее. Вообще, конечно, зависит от объёмов и требований.

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

PS: Пусть я нездоровый велосипедист, ну да ладно. :-)
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34654978
MBG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
MBG
Гость
Что-то говорили о триггере на Си? Начнем с того, что писать на Си триггер далеко не лучшая идея. А те специалисты, кто может это сделать грамотно, в подобных советах не нуждаются :-)

Далее, если есть заморочка с быстродействием (притом, что rule для сотен подтаблиц с разбиением например по месяцам отлично справляется), то необходимо использовать prepared запросы в триггере, иначе все будет еще медленнее. А приведенный вариант кроме большей заморочности пользы не принесет. Цифры быстродействия выглядят странно, на plpgsql такой скорости триггеров я не видел (тестил на PostgreSQL 8.0, сейчас сижу на 8.1, но уже не проверял).

Насчет 100 таблиц "не вижу препятствий"
foreach table {table1 table2 ... table100}
foreach year {06 07} {
foreach month {01 02 03 04 05 06 07 08 09 10 11 12}
вроде как можно догадаться, верно?

А насчет множества таблиц и проч. все делается малость не так. Пишется скрипт создания новой схемы БД "с нуля" (делаем дамп структуры, что нужно, выкидываем и заменяем скриптом) и в новую схему все переливается из старой.
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34655166
glebofff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MBGЧто-то говорили о триггере на Си? Начнем с того, что писать на Си триггер далеко не лучшая идея. :-)

Мдее. :-)
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34656193
Thamerlan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
glebofff
Код: plaintext
-- функция для копирования индексов

Кстати, если я ничего не упустил, то функция копирования индексов скопирует также индексы для PK и UNIQUE constraint'ов, то есть мы получим по два одинаковых индекса на каждый PK и UNIQUE.

Побороть можно немного изменив выборку индексов на:
Код: plaintext
1.
2.
3.
4.
5.
where idx.indrelid = srcoid and c.oid = idx.indexrelid
  AND  NOT c.relname IN (SELECT conname
                         FROM pg_constraint
                         WHERE conrelid = srcoid
                        );
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #34657274
glebofff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Thamerlan
Кстати, если я ничего не упустил, то функция копирования индексов скопирует также индексы для PK

Угу, спасибо. Для меня на тот момент было несмертельно. Двух одинаковых не должно быть, имена-то одинаковые. А так, конечно, надо отделять мух от котлет. :-)
...
Рейтинг: 0 / 0
Период между сообщениями больше года.
Партиционирование (нудно, сплошные сорцы :-)
    #38889057
glebofff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Спустя минувшие почти десять лет не могу, без сожаления, не констатировать нескольких фактов:

1. в юности я был, конечно, максималист, и связно рассказывать о своих идеях не умел,
2. не умею и сейчас,
3. статей о партиционировании по интернету - хренова туча, но так никто дальше не продвинулся,
4. моим "решением" люди пользуются до сих пор, и, наверное, стоит этот мутный поток переписать заново.

Сверху ссылка на триггеры битая, вот, выкладываю https://cloud.mail.ru/public/1ea876e69907/partitioning.tar.bz2
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #38889077
этта
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
glebofff,

посмотрите в исходники лондайста3, там люди изящно копируют констрайнты и индексы , просто делая в одном предложении как inherit так и like .... including all с предка.

кроме того, при инхерит постгрес наследует все прочие констрайнты сам, без ансам

а относительно новая опция ...constraint ... no inherit позволяет отделить одно от другого.

т.е. я пишу не like ... including all, а всё, без констрайнтов (которые заимствуются инхеритс-ом), а лайк мне бы ещё и no inherits скопировал

--такие вещи за 10 лет можно бы было приметить, что ли

ну и ещё, по мелочи.

но, посмотрел, ф-ии, судя по виду, -- полезные, т.ч. можете гордиццо
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #38890524
Фотография SmeL_md
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
glebofff4. моим "решением" люди пользуются до сих пор, и, наверное, стоит этот мутный поток переписать заново.В сырцах не заметил перенос привилегий GRANT.
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #38890589
этта
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
SmeL_mdglebofff4. моим "решением" люди пользуются до сих пор, и, наверное, стоит этот мутный поток переписать заново.В сырцах не заметил перенос привилегий GRANT.
помедитируйте над:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
SELECT string_agg(ddl_grant,E'\n') FROM 
	(SELECT 
		grantee
		,'GRANT '||  string_agg(privilege_type,',')
		||' ON ' || table_schema ||'.'||table_name|| ' TO '|| grantee ||';' AS ddl_grant
	FROM  information_schema. table_privileges 
	WHERE
		table_schema='my_schema'
		AND table_name = 'my_tabla'
	GROUP BY grantee,table_schema,table_name
	)
FOO;


т.е. фунцикло пишется на коленке за 5 15 минут.

а если посмотреть в кишки information_schema. table_privileges -- то можно ещё и много лишнего ручками снести.
...
Рейтинг: 0 / 0
Партиционирование (нудно, сплошные сорцы :-)
    #38891059
Фотография SmeL_md
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
этта,
так у меня есть :) но на has_table_privilege :)
...
Рейтинг: 0 / 0
18 сообщений из 18, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Партиционирование (нудно, сплошные сорцы :-)
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]