Numeric type
suminb opened this issue · comments
Outline
We are currently using a numeric(20, 4)
type to store monetary values, but numeric(10, 4)
should be enough. We would like to see such a change will decrease the amount of storage space needed and potentially increase the overall performance.
https://www.postgresql.org/docs/9.1/static/datatype-numeric.html
Counting the number of bytes per row
WITH x AS (
SELECT count(*) AS ct
, sum(length(t::text)) AS txt_len -- length in characters
, 'asset_value'::regclass AS tbl -- provide (qualified) table name here
FROM asset_value t -- ... and here
)
, y AS (
SELECT ARRAY [pg_relation_size(tbl)
, pg_relation_size(tbl, 'vm')
, pg_relation_size(tbl, 'fsm')
, pg_table_size(tbl)
, pg_indexes_size(tbl)
, pg_total_relation_size(tbl)
, txt_len
] AS val
, ARRAY ['core_relation_size'
, 'visibility_map'
, 'free_space_map'
, 'table_size_incl_toast'
, 'indexes_size'
, 'total_size_incl_toast_and_indexes'
, 'live_rows_in_text_representation'
] AS name
FROM x
)
SELECT unnest(name) AS what
, unnest(val) AS "bytes/ct"
, pg_size_pretty(unnest(val)) AS bytes_pretty
, unnest(val) / ct AS bytes_per_row
FROM x, y
UNION ALL SELECT '------------------------------', NULL, NULL, NULL
UNION ALL SELECT 'row_count', ct, NULL, NULL FROM x
UNION ALL SELECT 'live_tuples', pg_stat_get_live_tuples(tbl), NULL, NULL FROM x
UNION ALL SELECT 'dead_tuples', pg_stat_get_dead_tuples(tbl), NULL, NULL FROM x;
See https://dba.stackexchange.com/questions/23879/measure-the-size-of-a-postgresql-table-row for more details.
Result
what | bytes/ct | bytes_pretty | bytes_per_row
-----------------------------------+----------+--------------+---------------
core_relation_size | 4669440 | 4560 kB | 104
visibility_map | 8192 | 8192 bytes | 0
free_space_map | 24576 | 24 kB | 0
table_size_incl_toast | 4702208 | 4592 kB | 105
indexes_size | 4227072 | 4128 kB | 94
total_size_incl_toast_and_indexes | 8929280 | 8720 kB | 199
live_rows_in_text_representation | 5046614 | 4928 kB | 112
------------------------------ | | |
row_count | 44668 | |
live_tuples | 44668 | |
dead_tuples | 1186 | |
(11 rows)
Take 1
Plans
- Clone the current table (
asset_value
) asasset_value_cloned
. - Alter the
numeric(20, 4)
columns asnumeric(10, 4)
. - Run the same query to see if there is any sign of improvement.
Result
The entire table has been cloned as
CREATE TABLE asset_value_cloned AS SELECT * FROM asset_value
Interestingly, the table size has been shrunk. It's probably because there is no dead_tuples
in the cloned table.
List of relations
Schema | Name | Type | Owner | Size | Description
--------+--------------------+-------+--------+------------+-------------
public | asset_value | table | | 4592 kB |
public | asset_value_cloned | table | | 4408 kB |
As a result, the average row size got smaller. (104
-> 101
bytes)
what | bytes/ct | bytes_pretty | bytes_per_row
-----------------------------------+----------+--------------+---------------
core_relation_size | 4513792 | 4408 kB | 101
visibility_map | 0 | 0 bytes | 0
free_space_map | 0 | 0 bytes | 0
table_size_incl_toast | 4513792 | 4408 kB | 101
indexes_size | 0 | 0 bytes | 0
total_size_incl_toast_and_indexes | 4513792 | 4408 kB | 101
live_rows_in_text_representation | 5046614 | 4928 kB | 112
------------------------------ | | |
row_count | 44668 | |
live_tuples | 44668 | |
dead_tuples | 0 | |
(11 rows)
We altered the type of the numeric columns as follows.
ALTER TABLE asset_value_cloned ALTER open TYPE numeric(10, 4);
ALTER TABLE asset_value_cloned ALTER high TYPE numeric(10, 4);
ALTER TABLE asset_value_cloned ALTER low TYPE numeric(10, 4);
ALTER TABLE asset_value_cloned ALTER close TYPE numeric(10, 4);
No luck. The table size remains unaffected.
what | bytes/ct | bytes_pretty | bytes_per_row
-----------------------------------+----------+--------------+---------------
core_relation_size | 4513792 | 4408 kB | 101
visibility_map | 0 | 0 bytes | 0
free_space_map | 0 | 0 bytes | 0
table_size_incl_toast | 4513792 | 4408 kB | 101
indexes_size | 0 | 0 bytes | 0
total_size_incl_toast_and_indexes | 4513792 | 4408 kB | 101
live_rows_in_text_representation | 5046614 | 4928 kB | 112
------------------------------ | | |
row_count | 44668 | |
live_tuples | 44668 | |
dead_tuples | 0 | |
(11 rows)
Take 2
Plans
- Create a new, empty table with a new column type (
numeric(10, 4)
). - Copy over the entire data set into the new table.
Result
CREATE TABLE asset_value_cloned (
id bigint NULL,
asset_id bigint NULL,
base_asset_id bigint NULL,
evaluated_at timestamp without time zone NULL,
source asset_value_source NULL,
granularity granularity NULL,
open numeric(10,4) NULL,
high numeric(10,4) NULL,
low numeric(10,4) NULL,
close numeric(10,4) NULL,
volume integer NULL);
INSERT INTO asset_value_cloned SELECT * FROM asset_value;
what | bytes/ct | bytes_pretty | bytes_per_row
-----------------------------------+----------+--------------+---------------
core_relation_size | 4513792 | 4408 kB | 101
visibility_map | 0 | 0 bytes | 0
free_space_map | 24576 | 24 kB | 0
table_size_incl_toast | 4538368 | 4432 kB | 101
indexes_size | 0 | 0 bytes | 0
total_size_incl_toast_and_indexes | 4538368 | 4432 kB | 101
live_rows_in_text_representation | 5046614 | 4928 kB | 112
------------------------------ | | |
row_count | 44668 | |
live_tuples | 44668 | |
dead_tuples | 0 | |
(11 rows)
No changes in terms of bytes_per_row
after the alteration. We will put this one on halt and re-visit in the future.