Partitioning is your answer, as others stated, but:
I'd partition on some hash(a). If a is an integer then a%256 would be good. If it is a text then something like substring(md5(a) for 2).
It will speed up inserts and selects.
For deletes I'd make them run more often but smaller and also partitioned. I'd run them every hour (at XX:30) and like this:
delete from table_name
where date<(current_date - interval '1 year')
and
hash(a)
=
(extract(doy from current_timestamp) * 24
+ extract(hour from current_timestamp))::int % 256;
EDIT: I've just tested this:
create function hash(a text) returns text as $$ select substring(md5($1) for 1) $$ language sql immutable strict;
CREATE TABLE tablename (id text, mdate date);
CREATE TABLE tablename_partition_0 ( CHECK ( hash(id) = '0' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_1 ( CHECK ( hash(id) = '1' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_2 ( CHECK ( hash(id) = '2' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_3 ( CHECK ( hash(id) = '3' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_4 ( CHECK ( hash(id) = '4' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_5 ( CHECK ( hash(id) = '5' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_6 ( CHECK ( hash(id) = '6' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_7 ( CHECK ( hash(id) = '7' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_8 ( CHECK ( hash(id) = '8' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_9 ( CHECK ( hash(id) = '9' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_a ( CHECK ( hash(id) = 'a' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_b ( CHECK ( hash(id) = 'b' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_c ( CHECK ( hash(id) = 'c' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_d ( CHECK ( hash(id) = 'd' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_e ( CHECK ( hash(id) = 'e' ) ) INHERITS (tablename);
CREATE TABLE tablename_partition_f ( CHECK ( hash(id) = 'f' ) ) INHERITS (tablename);
analyze;
explain select * from tablename where id='bar' and hash(id)=hash('bar');
QUERY PLAN
---------------------------------------------------------------------------------------------
Result (cost=0.00..69.20 rows=2 width=36)
-> Append (cost=0.00..69.20 rows=2 width=36)
-> Seq Scan on tablename (cost=0.00..34.60 rows=1 width=36)
Filter: ((id = 'bar'::text) AND ("substring"(md5(id), 1, 1) = '3'::text))
-> Seq Scan on tablename_partition_3 tablename (cost=0.00..34.60 rows=1 width=36)
Filter: ((id = 'bar'::text) AND ("substring"(md5(id), 1, 1) = '3'::text))
(6 rows)
You'd need to add hash(id)=hash('searched_value') to your queries or Postgres will search all tables.
EDIT: You can also use rule system for automatic insertions to correct tables:
create rule tablename_rule_0 as
on insert to tablename where hash(NEW.id)='0'
do instead insert into tablename_partition_0 values (NEW.*);
create rule tablename_rule_1 as
on insert to tablename where hash(NEW.id)='1'
do instead insert into tablename_partition_1 values (NEW.*);
-- and so on
insert into tablename (id) values ('a');
select * from tablename_partition_0;
id | mdate
----+-------
a |
(1 row)