æ®å¿µãªãããPostgreSQLã®ããŒãã«ãããŒãã£ã·ã§ã³åå²ããæ©èœã¯ããããŸã§å€ãã®äººãç©æ¥µçã«äœ¿çšããŠããŸããã ç§ã®æèŠã§ã¯ãHubert LubaczewskiïŒ depesz.com ïŒã¯åœŒå¥³ã®ä»äºã«ã€ããŠéåžžã«äŸ¡å€ããããšèªã£ãŠããŸãã 圌ã®èšäºã®å¥ã®ç¿»èš³ãæäŸããŸãïŒ
æè¿ãç§ã¯ããŒãã£ã·ã§ã³åå²ã䜿çšã§ããã±ãŒã¹ã«åºãããããšãå€ãããšã«æ°ä»ããŸããã ãããŠãçè«çã«ã¯ãã»ãšãã©ã®äººã¯ãã®ååšãç¥ã£ãŠããŸãããå®éããã®æ©èœã¯ããç解ãããŠãããããããæããŠãã人ãããŸãã
ããã§ãç§ã®ç¥èãšèœåãæ倧éã«èª¬æãããããäœã§ããã®ãããªãããã䜿ãã¹ããªã®ããã©ããã£ãŠããã®ãã説æããããšæããŸãã
ãåç¥ã®ãšãããPostgreSQLã«ã¯ããŒãã«ããããããŒãã«ã«ã¯ããŒã¿ããããŸãã å Žåã«ãã£ãŠã¯ã»ãã®æ°è¡ã§ãããæã«ã¯æ°ååè¡ã§ãã
ããŒãã£ã·ã§ãã³ã°ã¯ãïŒåã§ã¯ãªãã¬ã³ãŒãã®æ°ã«åºã¥ããŠïŒå€§ããªããŒãã«ãå€ãã®å°ããªããŒãã«ã«åå²ããæ¹æ³ã§ãã ãããŠãããã¯ã¢ããªã±ãŒã·ã§ã³ã«å¯ŸããŠééçãªæ¹æ³ã§è¡ãããããšãæãŸããã§ãã
PostgreSQLã§ãã£ãã«äœ¿çšãããªãæ©èœã®1ã€ã¯ãPostgreSQLããªããžã§ã¯ããªã¬ãŒã·ã§ãã«ããŒã¿ããŒã¹ã§ããããšã§ãã ãŸãããªããžã§ã¯ãïŒãŸãã¯ã¯ã©ã¹ïŒããç¶æ¿ããšåŒã°ãããã®ãç¥ã£ãŠãããããããªããžã§ã¯ãããããŒã¯ãŒãã§ãã ãããããŒãã£ã·ã§ã³åã«äœ¿çšããããã®ã§ãã
ç§ãã¡ã話ããŠããããšãèŠãŠã¿ãŸãããã
éåžžã®ãŠãŒã¶ãŒããŒãã«ãäœæããŸãã
$ create table users ( id serial primary key, username text not null unique, password text, created_on timestamptz not null, last_logged_on timestamptz not null );
次ã«ãå³ãå®æãããããã«ãæ°è¡ãšè¿œå ã®ã€ã³ããã¯ã¹ãè¿œå ããŸãããã
$ insert into users (username, password, created_on, last_logged_on) select random_string( (random() * 4 + 5)::int4), random_string( 20 ), now() - '2 years'::interval * random(), now() - '2 years'::interval * random() from generate_series(1, 10000); $ create index newest_users on users (created_on);
ãããã£ãŠããã¹ãããŒãã«ããããŸãã
$ \d Table "public.users" Column | Type | Modifiers
ããã€ãã®ã©ã³ãã ããŒã¿ïŒ
$ select * from users limit 10; id | username | password | created_on | last_logged_on
ããŒãã«ã®æºåãã§ããã®ã§ãããŒãã£ã·ã§ã³ãäœæã§ããŸããã€ãŸããç¶æ¿ãããããŒãã«ãæå³ããŸãã
$ create table users_1 () inherits (users); $ \d users_1 Table "public.users_1" Column | Type | Modifiers
ãããã£ãŠãç¹å®ã®èå³æ·±ãããããã£ãæã€æ°ããããŒãã«ããããŸãã
- 圌女ã¯idåã«ã¡ã€ã³ããŒãã«ãšåãã·ãŒã±ã³ã¹ã䜿çšããŸãã
- éãã«å¶çŽãå«ããã¹ãŠã®åã®å®çŸ©ã¯åãã§ãã
- äž»ããŒããŠãŒã¶ãŒåã®äžææ§å¶éãcreated_onã®ã€ã³ããã¯ã¹ã¯ãããŸããã
ããäžåºŠè©ŠããŠã¿ãŸãããããä»åã¯ããççºçãªå¹æããããŸãã
$ drop table users_1; $ create table users_1 ( like users including all ); $ \d users_1 Table "public.users_1" Column | Type | Modifiers
ããã§ãã¹ãŠã®ã€ã³ããã¯ã¹ãšå¶éãã§ããŸããããç¶æ¿æ
å ±ã¯å€±ãããŸããã ãã ããåŸã§è¿œå ã§ããŸãã
$ alter table users_1 inherit users; $ \d users_1 Table "public.users_1" Column | Type | Modifiers
1ã€ã®ã¹ãããã§ãããè¡ãããšãã§ããŸããããã®åŸãããŸããŸãªäžå¿«ãªéç¥ã衚瀺ãããŸãã
$ drop table users_1; $ create table users_1 ( like users including all ) inherits (users); NOTICE: merging column "id" with inherited definition NOTICE: merging column "username" with inherited definition NOTICE: merging column "password" with inherited definition NOTICE: merging column "created_on" with inherited definition NOTICE: merging column "last_logged_on" with inherited definition $ \d users_1 Table "public.users_1" Column | Type | Modifiers
ãããã«ãããã¡ã€ã³ãšæåã®ããŒãã£ã·ã§ã³ã®2ã€ã®ããŒãã«ããããŸãã
ãŠãŒã¶ãŒã«å¯ŸããŠäœããã®ã¢ã¯ã·ã§ã³ïŒãã§ãã/æŽæ°/åé€ïŒãå®è¡ãããšãäž¡æ¹ã®ããŒãã«ãã¹ãã£ã³ãããŸãã
$ explain analyze select * from users where id = 123; QUERY PLAN
ãã ããããŒãã£ã·ã§ã³ã«çŽæ¥ã¢ã¯ã»ã¹ãããšããªã¯ãšã¹ãã¯ãã®ããŒãã£ã·ã§ã³ã§ã®ã¿å®è¡ãããŸãã
$ explain analyze select * from users_1 where id = 123; QUERY PLAN
å¿
èŠã«å¿ããŠãONLYããŒã¯ãŒãã䜿çšããŠããŒãã£ã·ã§ã³ãªãã§ãŠãŒã¶ãŒããŒãã«ã«ã®ã¿ã¢ã¯ã»ã¹ã§ããŸãã
$ explain analyze select * from only users where id = 123; QUERY PLAN
ãæ°ä»ããããããŸãããããã§ãã/æŽæ°/åé€ã¯ãã¹ãŠã®ããŒãã£ã·ã§ã³ã§æ©èœãããšèšã£ãŠããŸãã ã€ã³ãµãŒãã¯ã©ãã§ããïŒ æ¿å
¥ã¯ã©ããã«ããŒã¿ãè¿œå ããå¿
èŠããããããONLYã䜿çšãããŠãããã®ããã«åžžã«æ©èœããŸãã ãããã£ãŠãusers_1ã«è¡ãè¿œå ããå¿
èŠãããå Žåããããè¡ãå¿
èŠããããŸãã
INSERT INTO users_1 ...
ã©ãããèŠæ ãã¯ãããããŸããããå¿é
ããªãã§ãã ããã
å®éã®ããŒãã£ã·ã§ã³ãäœæããŠã¿ãŸãããã ãŸããããŒãã£ã·ã§ã³ããŒãäœã§ããããã€ãŸããããŒãã£ã·ã§ã³ãéžæãããã¢ã«ãŽãªãºã ã«ãã£ãŠæ±ºå®ããå¿
èŠããããŸãã
æãæçœãªãã®ãããã€ããããŸãã
- æ¥ä»ã«ããããŒãã£ã·ã§ã³-ããšãã°ããŠãŒã¶ãŒãäœæããã幎ã«åºã¥ããŠããŒãã£ã·ã§ã³ãéžæããŸãã
- ããŸããŸãªèå¥åã«ããããŒãã£ã·ã§ã³-ããšãã°ãæåã®100äžäººã®ãŠãŒã¶ãŒã200äžäººã®ãŠãŒã¶ãŒãªã©ã
- äœãå¥ã®ããŒãã£ã·ã§ã³-ããšãã°ããŠãŒã¶ãŒåã®æåã®æåã«ããããŒãã£ã·ã§ã³ã
ããŠãŒã¶ãŒã«ä»£ãã£ãŠããã·ã¥ããŒãã£ã·ã§ã³ããªã©ã䜿çšé »åºŠã®äœãä»ã®ãªãã·ã§ã³ãããã€ããããŸãã
ããã¹ããŒã ã䜿çšãã䟡å€ããããå¥ã®ã¹ããŒã ã䜿çšãã䟡å€ããªãã®ã¯ãªãã§ããïŒ ãããã®é·æãšçæãèŠãŠã¿ãŸãããã
- æ¥ä»ã«ããããŒãã£ã·ã§ã³ïŒ
- å©ç¹ïŒ
- ããããããã
- ãã®ããŒãã«ã®è¡æ°ã¯éåžžã«å®å®ããŠããŸãã
- æ¬ ç¹ïŒ
- ãµããŒããå¿
èŠ-æã
æ°ããããŒãã£ã·ã§ã³ãè¿œå ããå¿
èŠããããŸãã
- ãŠãŒã¶ãŒåãŸãã¯IDã§æ€çŽ¢ããã«ã¯ããã¹ãŠã®ããŒãã£ã·ã§ã³ãã¹ãã£ã³ããå¿
èŠããããŸãã
- IDã«ããããŒãã£ã·ã§ãã³ã°ïŒ
- å©ç¹ïŒ
- ããããããã
- ããŒãã£ã·ã§ã³å
ã®è¡æ°ã¯100ïŒ
å®å®ããŸãã
- æ¬ ç¹ïŒ
- ãµããŒããå¿
èŠ-æã
æ°ããããŒãã£ã·ã§ã³ãè¿œå ããå¿
èŠããããŸãã
- ãŠãŒã¶ãŒåãŸãã¯IDã§æ€çŽ¢ããã«ã¯ããã¹ãŠã®ããŒãã£ã·ã§ã³ãã¹ãã£ã³ããå¿
èŠããããŸãã
- ãŠãŒã¶ãŒåã®æåã®æåã«ããåå²ïŒ
- å©ç¹ïŒ
- ããããããã
- ãµããŒããªã-å³å¯ã«å®çŸ©ãããããŒãã£ã·ã§ã³ã®ã»ããããããæ°ããããŒãã£ã·ã§ã³ãè¿œå ããå¿
èŠã¯ãããŸããã
- æ¬ ç¹ïŒ
- ããŒãã£ã·ã§ã³å
ã®è¡æ°ã¯çå®ã«å¢å ããŸãã
- ããŒãã£ã·ã§ã³ã«ãã£ãŠã¯ãä»ã®ããŒãã£ã·ã§ã³ãããããªãå€ãã®è¡ããããŸãïŒãy *ãããããt *ãã§å§ãŸãããã¯ããŒã ãæã€äººãå€ãïŒã
- IDã§æ€çŽ¢ããã«ã¯ããã¹ãŠã®ããŒãã£ã·ã§ã³ãã¹ãã£ã³ããå¿
èŠããããŸãã
- ãŠãŒã¶ãŒåã®ããã·ã¥ã«ããåå²ïŒ
- å©ç¹ïŒ
- ãµããŒããªã-å³å¯ã«å®çŸ©ãããããŒãã£ã·ã§ã³ã®ã»ããããããæ°ããããŒãã£ã·ã§ã³ãè¿œå ããå¿
èŠã¯ãããŸããã
- è¡ã¯ããŒãã£ã·ã§ã³éã§åçã«åé
ãããŸãã
- æ¬ ç¹ïŒ
- ããŒãã£ã·ã§ã³å
ã®è¡æ°ã¯çå®ã«å¢å ããŸãã
- IDã§æ€çŽ¢ããã«ã¯ããã¹ãŠã®ããŒãã£ã·ã§ã³ãã¹ãã£ã³ããå¿
èŠããããŸãã
- ãŠãŒã¶ãŒåã«ããæ€çŽ¢ã§ã¯ã1ã€ã®ããŒãã£ã·ã§ã³ã®ã¿ãã¹ãã£ã³ãããŸãããè¿œå ã®æ¡ä»¶ã䜿çšãããå Žåã®ã¿ã§ãã
ããã·ã¥åããããŠãŒã¶ãŒåã¢ãããŒãã®æåŸã®æ¬ ç¹ã¯éåžžã«èå³æ·±ããã®ã§ãã ããã§äœãèµ·ãããèŠãŠã¿ãŸãããã
æåã«ãããã«ããŒãã£ã·ã§ã³ãäœæããå¿
èŠããããŸãã
$ create table users_2 ( like users including all ); $ alter table users_2 inherit users; ... $ create table users_10 ( like users including all ); $ alter table users_10 inherit users;
çŸåšãusersããŒãã«ã«ã¯10åã®ããŒãã£ã·ã§ã³ããããŸãã
$ \d users Table "public.users" Column | Type | Modifiers
PostgreSQLã«ã¯constraint_exclusionãªãã·ã§ã³ããããŸãã ãŸãããonããŸãã¯ãpartitionãã«èšå®ãããšãPostgreSQLã¯äžèŽããè¡ãå«ãããšãã§ããªãããŒãã£ã·ã§ã³ãã¹ãããããŸãã
ç§ã®Pgã§ã¯ãããã¯ããã©ã«ãã§èšå®ãããŠããŸãïŒ
$ show constraint_exclusion; constraint_exclusion
ãããã£ãŠããã¹ãŠã®ããŒãã£ã·ã§ã³ãšããŒã¹ããŒãã«ã«ã¯æå³ã®ããå¶éããªããããã¯ãšãªã¯ãã¹ãŠã®11ã®ããŒãã«ïŒã¡ã€ã³ããŒãã£ã·ã§ã³ãš10ããŒãã£ã·ã§ã³ïŒãäžåºŠã«ã¹ãã£ã³ããŸãã
$ explain analyze select * from users where id = 123; QUERY PLAN
ããã¯ããŸãå¹æçã§ã¯ãããŸããããå¶éãèšããããšãã§ããŸãã
ããŒãã£ã·ã§ã³ãIDã«ããããŒãã£ã·ã§ã³åã«ãã£ãŠåœ¢æãããåããŒãã£ã·ã§ã³ã«100,000åã®èå¥åãå«ãŸããŠãããšããŸãã
ããã€ãã®å¶éãè¿œå ã§ããŸãã
$ alter table users_1 add constraint partition_check check (id >= 0 and id < 100000); $ alter table users_2 add constraint partition_check check (id >= 100000 and id < 200000); ... $ alter table users_10 add constraint partition_check check (id >= 900000 and id < 1000000);
åã®ãªã¯ãšã¹ããç¹°ãè¿ããŸãïŒ
$ explain analyze select * from users where id = 123; QUERY PLAN
2ã€ã®ããŒãã«ã®ã¿ãã¹ãã£ã³ããŸããã¡ã€ã³ïŒãã¹ãŠã®ããŒã¿ãçŸåšãããå¶éããªããããé€å€ã§ããŸããïŒãšé©åãªããŒãã£ã·ã§ã³ã§ãã
ããã§ãã
ãã®ãããªããŒãã£ã·ã§ã³åæ¡ä»¶ã¯ããŠãŒã¶ãŒåãŸãã¯created_onã§ç°¡åã«è¿œå ã§ããŸãã ãã ããããŒãã£ã·ã§ã³ããŒãããè€éãªå Žåã¯ã©ããªãããèŠãŠãã ããã
$ alter table users_1 drop constraint partition_check, add constraint partition_check check (abs( hashtext(username) ) % 10 = 0); $ alter table users_2 drop constraint partition_check, add constraint partition_check check (abs( hashtext(username) ) % 10 = 1); ... $ alter table users_10 drop constraint partition_check, add constraint partition_check check (abs( hashtext(username) ) % 10 = 9);
ç¥ããªãå ŽåãhashtextïŒïŒã¯æååãåãåãã-2147483648ã2147483647ã®ç¯å²ã®æŽæ°ãè¿ããŸãã
åçŽãªç®è¡ã®ãããã§ãabsïŒhashtextïŒstringïŒïŒïŒ
10ã¯åžžã«0..9ã®ç¯å²ã®å€ãçæããã©ã®ãã©ã¡ãŒã¿ãŒã«ã€ããŠãç°¡åã«èšç®ã§ããããšãç¥ã£ãŠããŸãã
PostgreSQLã¯ãããç¥ã£ãŠããŸããïŒ
$ explain analyze select * from users where username = 'depesz'; QUERY PLAN
ãã ç¥ããŸããã åºæ¬çã«ãPostgreSQLã¯ç¯å²ïŒãŸãã¯åçïŒã«åºã¥ããæ€èšŒã®ããã«ããŒãã£ã·ã§ã³ãèªåçã«é€å€ããããšããã§ããŸããã æ©èœã«åºã¥ããã®ã¯ãããŸããã æ°åããã®åçŽãªã¢ãžã¥ãŒã«ã§ãããã§ã«å€ãããŸãïŒ
$ alter table users_1 drop constraint partition_check, add constraint partition_check check ( id % 10 = 0); $ alter table users_2 drop constraint partition_check, add constraint partition_check check ( id % 10 = 1); ... $ alter table users_10 drop constraint partition_check, add constraint partition_check check ( id % 10 = 9); $ explain analyze select * from users where id = 123; QUERY PLAN
ããã¯æ²ããã§ãã ãªããªããçªå·ã¢ãžã¥ãŒã«ã«åºã¥ããããŒãã£ã·ã§ã³ããŒã«ã¯ãïŒç§ã®æèŠã§ã¯ïŒ1ã€ã®å€§ããªå©ç¹ããããŸã-å®å®ããããŒãã£ã·ã§ã³æ°ã§ãã 倧éã®ããŒã¿ã«éãããšãã«å床ããŒãã£ã·ã§ã³ãäœæããããšã決å®ããªãéããå°æ¥ããããäœæããå¿
èŠã¯ãããŸããã
ããã¯ãè€éãªïŒæ°åã®é¢æ°ãŸãã¯ã¢ãžã¥ãŒã«ã«åºã¥ãïŒããŒãã£ã·ã§ã³ããŒã䜿çšã§ããªãããšãæå³ããŸããïŒ ãã ãããã䜿çšã§ããŸãããã¯ãšãªã¯ããè€éã«ãªããŸãã
$ explain analyze select * from users where id = 123 and id % 10 = 123 % 10; QUERY PLAN
ããã«ã次ã®ãããªå¥ã®æ¡ä»¶ãè¿œå ããŸããã
id % 10 = 123 % 10
PostgreSQLã¯ãåŒã解æããããã»ã¹ã§æžãæããããšãã§ããŸãã
id % 10 = 3
æŽæ°ã®ïŒ
æŒç®åãäžå€ã§ããããšãç¥ã£ãŠããããã§ãã ãããŠä»ãã¯ãšãªã®äžéšãšããŠãæ£ç¢ºãªããŒãã£ã·ã§ã³ããŒ-idïŒ
10 = 3ããããŸãããããã£ãŠãPgã¯ããŒãã£ã·ã§ã³ããŒïŒã€ãŸããããŒã¹ããŒãã«ïŒãæããªãããã¯ãšãªã«å¯Ÿå¿ããããŒãæã€ããŒãã£ã·ã§ã³ã®ã¿ã䜿çšã§ããŸãã ã
è¿œå ã®å䜵çãå°å
¥ãããã©ããã¯ããªã次第ã§ãã
ãªã¯ãšã¹ããå€æŽããããªãå ŽåããããŠæã
æ°ããããŒãã£ã·ã§ã³ãè¿œå ããã®ãé£ãããªãå Žåã¯ãç§ã®ä»¥åã®ååã§ããããŒã¹ã»ãã£ã¹ã±ãæžãã
PGããŒãã£ã·ã§ã³ãããŒãžã£ãŒã«æ
£ããå¿
èŠããããŸã-ããã¯ããŒãã£ã·ã§ã³ã決å®ããããã«æåã§å®è¡ããæ©èœã®ã»ããã§ããããã¯ã©ãŠã³ã§å®è¡ãããšãå°æ¥ã®ããŒã¿çšã«æ°ããããŒãã£ã·ã§ã³ãäœæãããŸãã
æ¿å
¥ã«ã€ããŠã¯ãã§ã«èšåããŸããããããŒãã£ã·ã§ã³ã«è¿œå ããå¿
èŠãããæ¿å
¥ã®åé¡ãåé¿ããæ¹æ³ã«ã€ããŠã¯èª¬æããŸããã§ããã
äžè¬ã«ãããã¯ããªã¬ãŒãžã§ãã§ãã äžåœã®Pg_partmanããããã®ããªã¬ãŒãäœæããŸãããäœãèµ·ãã£ãŠããã®ããç解ããpg_partmanããã©ãã¯ããã¯ã¹ãšããŠã§ã¯ãªããéå±ãªäœæ¥ãè¡ãè£å©ããŒã«ãšããŠäœ¿çšããŠãã ããã
çŸåšãç§ã®ããŒãã£ã·ã§ã³æ§æã¯æ°å€ããŒã¹ã®ã¢ãžã¥ãŒã«ã«åºã¥ããŠããŸãïŒç§ãç¥ãéããpartmanã¯ãããè¡ãããšãã§ããŸããïŒãé©åãªããªã¬ãŒé¢æ°ãäœæããŸãããã ããŒã¿ãusersããŒãã«ã«æ¿å
¥ããããšãã«åŒã³åºããããšã©ãŒãªãã§é©åãªããŒãã£ã·ã§ã³ã«æ¿å
¥ããªãã€ã¬ã¯ãããå¿
èŠããããŸãã ãããã£ãŠã次ã®ããã«èšè¿°ããŸãã
$ create function partition_for_users() returns trigger as $$ DECLARE v_parition_name text; BEGIN v_parition_name := format( 'users_%s', 1 + NEW.id % 10 ); execute 'INSERT INTO ' || v_parition_name || ' VALUES ( ($1).* )' USING NEW; return NULL; END; $$ language plpgsql;
ãããŠä»ãããªã¬ãŒã®å®çŸ©ïŒ
$ create trigger partition_users before insert on users for each row execute procedure partition_for_users();
è¡ãè¿œå ããŠã¿ãŸãããã
$ insert into users (username, password, created_on, last_logged_on) values ( 'depesz', random_string( 20 ), now() - '2 years'::interval * random(), now() - '2 years'::interval * random() ); $ select currval('users_id_seq'); currval
ããŒã¿ã衚瀺ããããã©ããèŠãŠã¿ãŸãããïŒ $ select * from users where username = 'depesz'; id | username | password | created_on | last_logged_on
ããããã§ãããã©ãã«ãããŸããïŒã¡ã€ã³ããŒãã«ã«ïŒ $ select * from only users where username = 'depesz'; id | username | password | created_on | last_logged_on
ãã
ããããé©åãªããŒãã£ã·ã§ã³ã«ããã®ã§ããããïŒ $ select * from users_4 where username = 'depesz'; id | username | password | created_on | last_logged_on
ã¯ã
ããªã¬ãŒã¯æ©èœããŸããããããããã®æ¹æ³ã«ã¯1ã€ã®æ¬ ç¹ããããŸããã€ãŸãããRETURNINGãã¯æ©èœããŸããã $ insert into users (username, password, created_on, last_logged_on) values ( 'test', random_string( 20 ), now() - '2 years'::interval * random(), now() - '2 years'::interval * random() ) returning *; id | username | password | created_on | last_logged_on
ããã¯ãããã©ãŒããŒã®èŠ³ç¹ãããæ¿å
¥ãäœãè¿ããªãã£ããããããªã¬ãŒãNULLãè¿ããããã«çºçããŸãããã®åé¡ã®è§£æ±ºçãèŠã€ããããšãã§ããŸããã§ãããç§ã®å ŽåãnextvalïŒïŒã䜿çšããŠäºåã«åæããŒå€ãååŸããŠãããå®æããå€ãæ¿å
¥ããããšã奜ã¿ãŸã-æ¿å
¥åŸã«æ¢ã«ååšããããã«ïŒ $ select nextval('users_id_seq'); nextval
ããã«ã¯1ã€ã®èª¬æããããŸããå¥ã®ãæ¿å
¥ãã®åã©ã€ã³PGãå¿
èŠã§ããå®è¡ãããŸãã®ã§ãããªã¬ãŒãä»ããŠãã¹ãŠã®ã€ã³ãµãŒããã«ãŒãã£ã³ã°ããããšã¯ãããããé
ããªããŸããç·æ¥ã®ãã«ã¯æ¿å
¥ã®ããã«æåã®è§£æ±ºçã¯ãããŒãã£ã·ã§ã³ãçŽæ¥æäœããããã«ããããååŸããããšã§ããããããã£ãŠãäŸãã°ã代ããã« COPY users FROM stdin; .... \.
æåã«ãããšãã°æ¬¡ã®ããã«ããŠãå¿
èŠãªèå¥åã®æ°ãææ¡ããŸãã select nextval('users_id_seq') from generate_series(1, 100);
ãããŠãé©åãªãã®ãçºè¡ããŸãã COPY users_p1 FROM stdin; .... \. COPY users_p2 FROM stdin; .... \. ...
æã䟿å©ãªæ¹æ³ã§ã¯ãããŸãããã倧éã®ããŒã¿ãããŒãã£ã·ã§ã³ããŒãã«ã«ã€ã³ããŒãããå Žåã«äŸ¿å©ã§ãããã®ãããä»åºŠã¯ããŒãã£ã·ã§ã³åãšã¯äœããã©ã®ããã«æ©èœããããç解ããå¿
èŠããããŸããã¿ã€ãã«ã®æ¬¡ã®è³ªåã¯ïŒãªãã§ããïŒçããã®ã¯æ¯èŒçç°¡åã§ããçç£æ§ã確ä¿ããããã¡ã³ããã³ã¹ãç°¡çŽ åããŸããç°¡åãªäŸãšããŠã10åè¡ïŒ1,000,000,000ïŒã®usersããŒãã«ãåãäžããŸããã€ã³ããã¯ã¹ã®æ·±ããå¢ãã ãã§ãã€ã³ããã¯ã¹ãèæ
®ããŠãããã®äžã®æ€çŽ¢ã¯åŸã
ã«äŸ¡æ ŒãäžæããŸããããã¯ç§ã®å°ããªãã¹ããã¿ãŒã³ã§ãèŠãããšãã§ããŸãããã¹ãŠã®ããŒãã£ã·ã§ã³ãšããŒãã£ã·ã§ã³ããªã¬ãŒããªã»ããããŸãããã $ drop table users_1; $ drop table users_2; ... $ drop table users_10; $ drop trigger partition_users on users;
çŸåšããŠãŒã¶ãŒããŒãã«ã«ã¯10,000è¡ãããŸãããŠãŒã¶ãŒåã«ããåçŽãªæ€çŽ¢ã«ã¯0.020ããªç§ããããŸããããã¯3åã®è©Šè¡ã®ãã¡ã§æé©ãªæéã§ããè¡ãè¿œå ããå ŽåïŒ $ insert into users (username, password, created_on, last_logged_on) select random_string( (random() * 4 + 5)::int4), random_string( 20 ), now() - '2 years'::interval * random(), now() - '2 years'::interval * random() from generate_series(1, 100000);
åãæ€çŽ¢ã«ã¯0.025msããããŸããæ€çŽ¢æéã0.005mså¢ããããšã¯å°ãããããããŸãããããŸã 110,000è¡ãããªããããã·ã¹ãã ã«ã¯ä»ã®ããŒãã«ããªããããã€ã³ããã¯ã¹ä»ãã®ããŒãã«å
šäœãã¡ã¢ãªã«æ ŒçŽãããŸãããã¡ãããããŒãã£ã·ã§ã³åå²ã«ã¯æå³ããããŸããããšãã°ãéåžžãŠãŒã¶ãŒåã§æ€çŽ¢ããå ŽåãIDã§ããŒãã£ã·ã§ã³ãäœæããŠãæå³ããããŸãã-ãã¹ãŠã®ããŒãã£ã·ã§ã³ã§Pgãæ€çŽ¢ããå¿
èŠããããŸãïŒããã¯å°æ¥çã«æå³ããããŸããããã®èšäºã®æåŸã§èª¬æããŸãïŒãã€ãŸããéåžžã¯äœãå°ãããã決å®ããå¿
èŠããããŸãããããäœããã®ããŒã«ããæ€çŽ¢ãªã®ããããããææ°ã®ããŒã¿ã®ã¿ã衚瀺ããã®ãããšããããšã§ãããããŠãPgãã¹ãã£ã³ããå¿
èŠãããããŒãã£ã·ã§ã³ã®æ°ãå¶éãããããªæ¹æ³ã§ããŒãã£ã·ã§ã³ãäœæããŸããéèŠãªããšã¯ãç¹ã«ããã°ã©ããããããŒã¿ããŒã¹ç®¡çè
ã§ããå Žåã¯ãããŒãã£ã·ã§ã³åã«ããäœæ¥ã楜ã«ãªãããšã§ããã¡ã³ããã³ã¹ã¿ã¹ã¯ïŒã€ã³ããã¯ã¹ã®äœæãããã¥ãŒã ãpg_reorg / pg_repackãpg_dumpïŒã¯ãããŒãã£ã·ã§ã³ãšåãæ°ã®ãµãã¿ã¹ã¯ã«å¹æçã«åå²ã§ããŸãããããã£ãŠã倧ããªããŒãã«ãåããã±ãŒãžåããããã®1æéã®ãã©ã³ã¶ã¯ã·ã§ã³ã®ä»£ããã«ã20ã®é«éåãšå°ãªããã£ã¹ã¯ã¹ããŒã¹ãã©ã³ã¶ã¯ã·ã§ã³ã®äœ¿çšãå¯èœã«ãªããçµæã¯äžè¬ã«åãã«ãªããŸãïŒãã¡ãããããžãã¹ã¯è¯ããã¥ãŒã¹ã«éå®ãããŸãããããŒãã£ã·ã§ã³åã«ã¯1ã€ã®å€§ããªæ¬ ç¹ããããŸããããŒãã£ã·ã§ã³åãããããŒãã«ãæãå€éšããŒãæã€ããšã¯ã§ããŸãããããŸããããŸãããããŒãã£ã·ã§ã³ãçŽæ¥æãå€éšããŒãååŸããããšãã§ããŸãããããã¯ïŒéåžžïŒç¡æå³ã§ãããããå人ã«ãšã£ãŠå€§ããªåé¡ã§ãããã©ããã¯ããŠãŒã¶ãŒã±ãŒã¹ã«ãã£ãŠç°ãªããŸããã»ãšãã©ã®å ŽåãããŒãã£ã·ã§ãã³ã°ãæ£åœåãããã»ã©ååã«å€§ããªããŒãã«ã«å°éãããšãã¢ããªã±ãŒã·ã§ã³ã¯ååã«ãã¹ããããå€éšããŒã®æ¬ åŠãåãå
¥ããããšãã§ããããã«æããŸããããã«ããæªããå€ã®ååšããã¹ãããã¿ã¹ã¯ããã€ã§ãã¯ã©ãŠã³ã«è¿œå ã§ããŸããããã§ãããŒãã£ã·ã§ãã³ã°ãšã¯äœããã©ã®ããã«æ©èœãããªã䜿çšãããããããããŸãããæåŸã®çåã¯ãããŒãã«ãããŒãã£ã·ã§ã³åå²ãããããŒãã«ã«å€æããæ¹æ³ã§ããéåžžãã¢ããªã±ãŒã·ã§ã³ã¯ããŒãã£ã·ã§ã³ããŒãã«ã§äœæãããŸãã-æåã¯ããã¯æå³ããããŸãããããããããã«ãå€ãã®è¡ãå«ãäœããã®çš®é¡ã®ããŒãã«ãäœæããããäœææã«ããã«ããŒãã£ã·ã§ã³ãäœæããå¿
èŠããããŸããããšæãããã«ãªããŸãããã ããã¢ããªã±ãŒã·ã§ã³ãæ¢ã«å®è¡ãããŠããå Žåã§ããããŒãã£ã·ã§ã³ãåå²ã§ããŸããïŒæå°éã®åé¡ã§ïŒèŠãŠã¿ãŸãããã
ãã¹ãã®ããã«ã97 GBã®pgbenchããŒã¿ããŒã¹ãäœæããŸããããã®ã»ãšãã©ã83 GBã¯ã666,600,000ã®ãšã³ããªãå«ãpgbench_accountsããŒãã«ã«ãããŸãããã®ããŒãã«ã®ã¹ããŒã ã¯æ¬¡ã®ãšããã§ãã Table "public.pgbench_accounts" Column | Type | Modifiers
ãããŠãããã«å¯Ÿãããã¹ãŠã®èŠæ±ã¯ã1ãã666,600,000ã®å€ãå«ãæŽå©åã«åºã¥ããŠããŸããããã§ã¯ãæŽå©å€ã®ç¯å²ã«åºã¥ããŠåå²ããŸããããåããŒãã£ã·ã§ã³ã«1,000äžè¡ãé
眮ãããšã67åã®ããŒãã£ã·ã§ã³ãå¿
èŠã«ãªããŸããããããèªåã®ã¢ã¯ã·ã§ã³ãäœæ¥ãäžæããªãããšãã©ã®ããã«ç¢ºèªã§ããŸããïŒãšãŠãç°¡åã§ãã
pgbenchãã«ãŒãã§å®è¡ããŸããæ£ç¢ºãªé床ã¬ããŒãã«ã¯èå³ããããŸãã; pgbenchã®åäœã«ç§ã®äœæ¥ãã©ã®çšåºŠåœ±é¿ãããã«ã€ããŠã®ååãªæ
å ±ããããŸãããããã®èãã§ãç§ã¯é¢æ°ãå®è¡ããŸããïŒ $ while true do date pgbench -T 10 -c 2 bench done 2>&1 | tee pgbench.log
圌女ã¯10ç§ã®ãã¹ããå®è¡ããçµ±èšããã¡ã€ã«ã«ä¿åããã®ã§ãåŸã§çµæãšããŒãã£ã·ã§ã³åå²äœæ¥ã®é¢ä¿ã远跡ã§ããŸãããã¹ãŠã®æºåãã§ããããé©åãªå Žæã«ãã§ãã¯ãå
¥ããŠããŒãã£ã·ã§ã³ãäœæããŸãã do $$ declare i int4; aid_min INT4; aid_max INT4; begin for i in 1..67 loop aid_min := (i - 1) * 10000000 + 1; aid_max := i * 10000000; execute format('CREATE TABLE pgbench_accounts_p_%s ( like pgbench_accounts including all )', i ); execute format('ALTER TABLE pgbench_accounts_p_%s inherit pgbench_accounts', i); execute format('ALTER TABLE pgbench_accounts_p_%s add constraint partitioning_check check ( aid >= %s AND aid <= %s )', i, aid_min, aid_max ); end loop; end; $$;
ããŒãã£ã·ã§ã³ã®æºåãã§ããŠããããã§ãã¯ã䜿çšãããŠããããšã確èªã§ããŸãã $ explain analyze select * from pgbench_accounts where aid = 123; QUERY PLAN
次ã«ãããªã¬ãŒãã«ãŒã¿ãŒããè¿œå ããå¿
èŠããããŸãã $ create function partition_for_accounts() returns trigger as $$ DECLARE v_parition_name text; BEGIN v_parition_name := format( 'pgbench_accounts_p_%s', 1 + ( NEW.aid - 1 ) / 10000000 ); execute 'INSERT INTO ' || v_parition_name || ' VALUES ( ($1).* )' USING NEW; return NULL; END; $$ language plpgsql; $ create trigger partition_users before insert on pgbench_accounts for each row execute procedure partition_for_accounts();
ããã¯ãã¹ãŠçŽ æŽãããããšã§ãããæ°ããæ¿å
¥ãããè¡ã«å¯ŸããŠã®ã¿æ©èœãããœãŒã¹ããŒãã«ã«ã¯æ¢ã«6å6600äžè¡ãããŸããããã«ã€ããŠã©ãããã°ããã§ããïŒãããã移åããå¿
èŠããããŸããããã¯çè«çã«ã¯æ¯èŒçåçŽã§ãããããã€ãã®èœãšãç©ŽããããŸãã- ã©ã®ãããªå Žåã§ããã©ã®ãã©ã³ã¶ã¯ã·ã§ã³ã§ãïŒã€ãŸããã¡ã€ã³ããŒãã«ãšããŒãã£ã·ã§ã³ããïŒäž¡æ¹ã®è¡ãåæã«è¡šç€ºãããããšã¯ãããŸããã
- ãã¹ãŠã®è¡ãåé€ããŠããŒãã£ã·ã§ã³ã«æ¿å
¥ããããšã¯ã§ããŸãããããã«ããã移åäžã«ããŒã¹ããŒãã«å
šäœãããã¯ãããããã§ãã
2çªç®ã®åé¡ã¯ãããŒã¿ããããæäœããããšã§è»œæžã§ããŸãããã ããããã«ã¯SQLã䜿çšã§ããŸãããæã
ã誰ãã倧èŠæš¡ãªæäœããã£ã³ã¯ã«åå²ããããŒã¿ã®ãã£ã³ã¯ãç¹°ãè¿ãåŠçããåäžã®sqlé¢æ°ã§åŒã³åºãæ¹æ³ãå°ããŸãããã®ã¢ãããŒãã«ã¯1ã€ã®åºæ¬çãªåé¡ããããŸããé¢æ°åŒã³åºãã¯ãã©ã³ã¶ã¯ã·ã§ã³ã§ãããããã£ãŠããã®é¢æ°ãå®è¡ãããã¹ãŠã¯ãåäžã®ãã©ã³ã¶ã¯ã·ã§ã³ã§çºçããŸãããããã£ãŠãããã¯ã®åé¡ã¯è§£æ±ºãããŸããããã ããpsqlã䜿çšããŠïŒãŸãã¯rubyãperlãpython-é¢ä¿ãããŸããïŒãåéšåã§å°æ°ã®è¡ã®ã¿ã移åããã¡ã€ã³ããŒãã«ãçæéããã¯ããŸããäžè¬ã«ãåäžã®ãªã¯ãšã¹ãã¯æ¬¡ã®ããã«ãªããŸãã with x as (delete from only pgbench_accounts where aid between .. and .. returning *) insert into appropriate_partition select * from x;
ãµãŒãã³ã°ãµã€ãº1000ãéžæããŸãããããã¯ãããã»ã¹ãåŒãããããªãããã«ååå°ããããµãŒãã³ã°ã®ç·æ°ãé床ã«ãªããªãããã«ååã«å€§ããïŒ666åïŒã§ããããã§ã¯ãããããã¡ã€ã«ãäœæããŸãããã \pset format unaligned \pset tuples_only true \o /tmp/run.batch.migration.sql SELECT format( 'with x as (DELETE FROM ONLY pgbench_accounts WHERE aid >= %s AND aid <= %s returning *) INSERT INTO pgbench_accounts_p_%s SELECT * FROM x;', i, i + 999, ( i - 1 ) / 10000000 + 1 ) FROM generate_series( 1, 666600000, 1000 ) i; \o
ãããpsqlã§å®è¡ãããšã圌ã¯/tmp/run.batch.migration.sqlãã¡ã€ã«ãäœæããŸãããããã¯ã次ã®ãããª666,600ã®ã¯ãšãªãå«ãŸããŠãããããéåžžã«å€§ããïŒ97 GBïŒã§ãã with x as (DELETE FROM ONLY pgbench_accounts WHERE aid >= 1 AND aid <= 1000 returning *) INSERT INTO pgbench_accounts_p_1 SELECT * FROM x; with x as (DELETE FROM ONLY pgbench_accounts WHERE aid >= 1001 AND aid <= 2000 returning *) INSERT INTO pgbench_accounts_p_1 SELECT * FROM x; with x as (DELETE FROM ONLY pgbench_accounts WHERE aid >= 2001 AND aid <= 3000 returning *) INSERT INTO pgbench_accounts_p_1 SELECT * FROM x;
ãã¹ãŠã®æºåãæŽã£ãã®ã§ãããã»ã¹ãéå§ã§ããŸãïŒãã¡ããããscreenããŸãã¯ãtmuxãã䜿çšããŠããµãŒããŒãžã®sshæ¥ç¶ãåæãããŠãäœã倱ãããªãããã«ããŸãïŒã $ psql -d bench -f /tmp/run.batch.migration.sql
å°ãæéãããããŸãããã¹ãããŒã¿ããŒã¹ã®å Žåãå¹³åãã±ããã¯çŽ92ããªç§ã§åŠçãããŸããã€ãŸããããŒã¿ãå
ã«é²ããã®ã«17æéå¿
èŠã§ããçŸå®ã«ã¯ããã£ã7æéããããããŸããã§ãããæªããªãã
æåŸã«ãpgbench_accountsããŒãã«ã®ééã¯çŽ83GBã§ãïŒpgbenchãrelocationãvacuumãåŠçããã®ã«ååãªé床ã®ãã£ã¹ã¯ã§ã¯ãªããšæããŸãïŒãããããç§ã¯ãã§ãã¯ãããã¹ãŠã®è¡ãããŒãã£ã·ã§ã³ã«ç§»åããããã«èŠããŸãïŒ $ select count(*) from only pgbench_accounts; count
移åããã»ã¹äžã®pgbenchã®é床ã¯ã©ãã§ããïŒ4ã€ã®ãã§ãŒãºããããŸããã- ä»äºã移ãåã
- ããŒãã£ã·ã§ã³ãäœæããåŸã
- ããªã¬ãŒãäœæããåŸã
- 移åäžã
çµæã¯ïŒ phase | min | avg | max
ã¯ãã移åãããšãã¹ãŠãé
ããªããŸãããããããããã¯SSDã§ã¯ãªãSATAãã£ã¹ã¯ãåããéåžžã®ããŒãœãã«ã³ã³ãã¥ãŒã¿ã§ãããåžžã«é«è² è·äžã«ããããšã«æ³šæããŠãã ãã-pgbenchã¯ã§ããã ãéããªã¯ãšã¹ããå®è¡ããŸãããããã«ãããã¥ãŒã ãåé€ãé©åã«åŠçã§ããªããããããããªé床äœäžãçºçããŸãããç§ã®æèŠã§ã¯ãçµæã¯çµ¶å¯Ÿã«åãå
¥ããããŸããæåŸã«ãç§ã¯æ¬¡ã®ããšãã§ããŸããïŒ $ truncate only pgbench_accounts;
ãããŠããã¹ãŠãæ£åžžãã©ããã確èªããã«ã¯ïŒ $ select count(*) from pgbench_accounts; count
ãããã¯ãã¹ãŠãšã©ãŒãªãã§ããå®éã®ã¢ããªã±ãŒã·ã§ã³ãã®äœæ¥ãäžæããããšãªãè¡ãããŸãããæåŸã«ãããŒãã£ã·ã§ã³åå²ãïŒæ¯èŒçïŒããã«ããã«ã¯ãŒã«ã«ãªãããšãè¿œå ããŸããæè¿ãããŒãã£ã·ã§ã³ãç°ãªããµãŒããŒã«ä¿åã§ããŸãããããŠçŸåšã䞊åã¹ãã£ã³ãæå¹ã«ããããã®äœæ¥ãé²è¡äžã§ãïŒãã ãããã®æŽæ°ãããŒãžã§ã³9.6ããåã«è¡šç€ºãããããšã¯ã»ãšãã©ãããŸããïŒãããã«ãããããã»ã¹å
šäœã倧å¹
ã«æ¹åãããŸãããã®ããã¹ããã圹ã«ç«ãŠã°å¹žãã§ããPostgreSQLã§ã®ããŒãã«ããŒãã£ã·ã§ã³åå²ã®ãã®ä»ã®åŽé¢ã«ã€ããŠèª¬æããŸããïŒPG Day'16 Russiaã«ã³ãã¡ã¬ã³ã¹ã®ã¬ããŒãã®ããã°ã©ã ã«ãããªãã«ãšã£ãŠæãèå³æ·±ããããã¯ãè¿œå ã§ããããšãå¬ããæããŸãïŒã¢ãŒãªãŒããŒããã±ããã®è²©å£²ãæ¢ã«éå§ããŠããŸã æ¥ãã§æäœäŸ¡æ Œã§ç»é²ããŸãããïŒ