GreenPlum中的分区表在数据量较大的情况下对于提升查询性能的帮助非常的,但是GreenPlum本身并没有提供分区表自动维护的工具,这里我们利用GreenPlum的PL/SQL自定义两个分区表自动维护的存储过程(也可以成为函数)。

  创建存储过程之前首先要创建一个记录分区表详细信息的视图,这里可以参见上篇博文。由于业务中有多张表需要做分区,而且分区字段的类型并不一样,因此我们首先创建一张字典表,记录每张表的分区类型,如下:

  1. CREATE TABLE op_tb_partition (
  2.   tb_name varchar(100) DEFAULT NULL,
  3.   timetype varchar(8) DEFAULT NULL
  4. )DISTRIBUTED BY (tb_name);
  5. select * from op_tb_partition;                                                                                                 
  6.            tb_name           | timetype 
  7. -----------------------------+----------
  8.  nl_app_action_error_trace   | unixtime
  9.  nl_mob_app_anr_data         | usertime
  10.  nl_mob_app_error_trace_test | datetime
  11. (3 rows)

  三张表的表结构以及分区分析如下: 

  1. create table nl_app_action_error_trace (
  2.   id bigserial not null,
  3.   timestamp int not null,
  4.   application_id int not null,
  5.   application_instance_id int not null,
  6.   action_id int not null,
  7.   action_type int not null,
  8.   key_action_id int not null,
  9.   error_type int not null,
  10.   error_message varchar(1025default null,
  11.   error_count int not null,
  12.   request_uri varchar(1024default null,
  13.   trace_data varchar(512default null
  14. )WITH (appendonly=true, orientation=column,compresstype=zlib, compresslevel=5DISTRIBUTED BY (id,timestamp,application_id)
  15. PARTITION BY RANGE (timestamp)
  16. (
  17. partition p20160701 start (1467302400::intend (1467388800::int),
  18. partition p20160702 start (1467388800::intend (1467475200::int)
  19. );
  20. create table nl_mob_app_error_trace_test (
  21.   id bigserial not null,
  22.   timestamp timestamp not null,
  23.   mobile_app_id int not null,
  24.   mobile_app_version_id int not null,
  25.   manufacturer_id int not null,
  26.   manufacturer_model_id int not null,
  27.   os_id int not null,
  28.   os_version_id int not null,
  29.   agent_version_id int not null default 0,
  30.   country_id int not null,
  31.   region_id int not null,
  32.   carrier_id int not null,
  33.   connect_type_id int not null,
  34.   key_url_id int not null,
  35.   ip bigint not null default 0,
  36.   host_id int not null,
  37.   host_ip bigint not null default 0,
  38.   error_type int not null,
  39.   error_code int not null,
  40.   error_count int not null,
  41.   request_url_id int not null,
  42.   request_url varchar(1024) DEFAULT NULL,
  43.   trace_data varchar(512) DEFAULT NULL
  44. )WITH (appendonly=true, orientation=column,compresstype=zlib, compresslevel=5DISTRIBUTED BY (id,timestamp,mobile_app_id)
  45. PARTITION BY RANGE (timestamp)
  46. (
  47. partition p20160701 start ('2016-07-01'::dateend ('2016-07-02'::date),
  48. partition p20160702 start ('2016-07-02'::dateend ('2016-07-03'::date)
  49. );
  50. CREATE TABLE NL_MOB_APP_ANR_DATA (
  51.   id bigserial NOT NULL ,
  52.   timestamp int NOT NULL,
  53.   timestamp_anr int NOT NULL,
  54.   mobile_app_id int NOT NULL,
  55.   mobile_app_version_id int NOT NULL,
  56.   manufacturer_id int NOT NULL,
  57.   manufacturer_model_id int NOT NULL,
  58.   os_id int NOT NULL,
  59.   os_version_id int NOT NULL,
  60.   carrier_id int NOT NULL,
  61.   connect_type_id int NOT NULL,
  62.   agent_version_id int NOT NULL,
  63.   device_id bigint NOT NULL,
  64.   mobile_anr_id bigint NOT NULL DEFAULT 0,
  65.   anr_message varchar(1024) DEFAULT NULL,
  66.   stacktrace varchar(512) DEFAULT NULL ,
  67.   stacktrace_deobfuscated varchar(512) DEFAULT NULL,
  68.   anr_trace_file varchar(512) DEFAULT NULL,
  69.   anr_system_log varchar(512) DEFAULT NULL,
  70.   additional_info varchar(512) DEFAULT NULL
  71. )WITH (appendonly=true, orientation=column,compresstype=zlib, compresslevel=5DISTRIBUTED BY (id,timestamp,mobile_app_id)
  72. PARTITION BY RANGE (timestamp)
  73. (
  74. partition p20160701 start (1838400::intend (1839840::int),
  75. partition p20160702 start (1839840::intend (1841280::int)
  76. );

  创建添加分区的存储过程,GreenPlum中单引号的转义符为两个单引号,详细代码如下:

  1. create or replace function add_partition_day() 
  2. returns text as
  3. $$
  4. declare tb_options record;
  5. declare curr_part varchar(8);
  6. declare max_part varchar(20);
  7. declare part_name varchar(9);
  8. declare end_part varchar(8);
  9. declare start_range int;
  10. declare end_range int;
  11. declare x int;
  12. begin
  13. for tb_options in select * from   op_tb_partition group by tb_name,timetype loop
  14. if tb_options.timetype = 'datetime' then
  15. select replace(substring(current_date + interval '7 day' from 1 for 10),'-',''as date into max_part;
  16. select substring(max(partition_name) from 2 for 8) into curr_part from  v_gp_range_partition_meta a join pg_class b on a.table_name=b.oid where b.relname =tb_options.tb_name;
  17. select date(max_part) - date(curr_part) into x;
  18. while x > 0 loop
  19. select replace(substring(date(curr_part) + interval '1 day' from 1 for 10),'-',''as date into curr_part;
  20. select replace(substring(date(curr_part) + interval '1 day' from 1 for 10),'-',''as date into end_part;
  21. select 'p' || curr_part into part_name;
  22. execute 'alter table ' ||  tb_options.tb_name || ' add partition ' || part_name || ' start (''' || curr_part || '''::date) end (''' || end_part || '''::date) with (appendonly=true, orientation=column,compresstype=zlib, compresslevel=5)'
  23. x = x-1;
  24. end loop;
  25. end_part = '';
  26. end_range = 0;
  27. elsif tb_options.timetype = 'usertime' then
  28. select replace(substring(current_date + interval '7 day' from 1 for 10),'-',''as date into max_part;
  29. select substring(max(partition_name) from 2 for 8) into curr_part from  v_gp_range_partition_meta a join pg_class b on a.table_name=b.oid where b.relname =tb_options.tb_name;
  30. select date(max_part) - date(curr_part) into x;
  31. while x > 0 loop
  32. select replace(substring(date(curr_part) + interval '1 day' from 1 for 10),'-',''as date into curr_part;
  33. select replace(substring(date(curr_part) + interval '1 day' from 1 for 10),'-',''as date into end_part;
  34. select nl_to_timestamp(date(curr_part)) into start_range;
  35. select nl_to_timestamp(date(end_part)) into end_range;
  36. select 'p' || curr_part into part_name;
  37. execute 'alter table ' ||  tb_options.tb_name || ' add partition ' || part_name || ' start (' || start_range || '::int) end (' || end_range || '::int)  with (appendonly=true, orientation=column,compresstype=zlib, compresslevel=5)'
  38. -- alter table tb_options.tb_name add partition part_name start (start_range::intend (end_range::int);
  39. x = x-1;
  40. end loop;
  41. end_part = '';
  42. end_range = 0;
  43. elsif tb_options.timetype = 'unixtime' then
  44. select replace(substring(current_date + interval '7 day' from 1 for 10),'-',''as date into max_part;
  45. select substring(max(partition_name) from 2 for 8) into curr_part from  v_gp_range_partition_meta a join pg_class b on a.table_name=b.oid where b.relname =tb_options.tb_name;
  46. select date(max_part) - date(curr_part) into x;
  47. while x > 0 loop
  48. select replace(substring(date(curr_part) + interval '1 day' from 1 for 10),'-',''as date into curr_part;
  49. select replace(substring(date(curr_part) + interval '1 day' from 1 for 10),'-',''as date into end_part;
  50. select unix_timestamp(date(curr_part)) into start_range;
  51. select unix_timestamp(date(end_part)) into end_range;
  52. select 'p' || curr_part into part_name;
  53. execute 'alter table ' ||  tb_options.tb_name || ' add partition ' || part_name || ' start (' || start_range || '::int) end (' || end_range || '::int)  with (appendonly=true, orientation=column,compresstype=zlib, compresslevel=5)'
  54. -- alter table tb_options.tb_name add partition part_name start (start_range::intend (end_range::int);
  55. x = x-1;
  56. end loop;
  57. end_part = '';
  58. end_range = 0;
  59. end if;
  60. end loop; 
  61. return 'ok';
  62. end;
  63. $$
  64. LANGUAGE plpgsql;

  创建删除分区的存储过程,这里的数据保存3个月,详细代码如下:

  1. create or replace function drop_partition_day() 
  2. returns text as
  3. $$
  4. declare tb_options record;
  5. declare const_part int default 90+7;
  6. declare part_count int;
  7. declare min_part_name varchar(255);
  8. declare x int;
  9. begin
  10.     for tb_options in select * from   op_tb_partition group by tb_name,timetype loop
  11.       select count(*) into part_count from v_gp_range_partition_meta a join pg_class b on a.table_name=b.oid where b.relname = tb_options.tb_name;
  12.         select part_count - const_part into x;
  13.         while x > 0 loop
  14.             select min(a.partition_name) into min_part_name from v_gp_range_partition_meta a join pg_class b on a.table_name=b.oid where b.relname = tb_options.tb_name;
  15.             execute 'alter table ' || tb_options.tb_name || ' drop partition ' || min_part_name;
  16.             x = x -1;
  17.         end loop;
  18.         part_count = 0;
  19.         min_part_name = '';
  20.         x = 0;
  21.     end loop; 
  22.     return 'ok';
  23. end;
  24. $$
  25. LANGUAGE plpgsql;

  调用声明好的存储过程,如下:

  1. testdb=# select add_partition_day();                                                                                                                           
  2.  add_partition_day 
  3. -------------------
  4.  ok
  5. (1 row)
  6. testdb=# select drop_partition_day();
  7.  drop_partition_day 
  8. --------------------
  9.  ok
  10. (1 row)