问题
I want to migrate a column from timestamp
(no timezone) to timestamptz
type.
I'm on Postgres 9.3.9.
I need to know if this operation will cause a table rewrite (lock the table) as my table is large and the db is live.
I found this in the 9.2 release notes:
Increasing the length limit for a varchar or varbit column, or removing the limit altogether, no longer requires a table rewrite. Similarly, increasing the allowable precision of a numeric column, or changing a column from constrained numeric to unconstrained numeric, no longer requires a table rewrite. Table rewrites are also avoided in similar cases involving the interval, timestamp, and timestamptz types.
It sounds promising but doesn't actually detail what 'similar cases' might be.
If this operation is going to lock the table I'd appreciate suggestions for how to work around this on a live db without interrupting service.
回答1:
First of all, you seem to be confusing locks and table rewrites. The note in the release notes talks about table rewrites - which always take an ACCESS EXCLUSIVE lock on the table. But here are many other operations that also take locks.
You would need:
ALTER TABLE tbl ALTER ts_col TYPE timestamptz;
Unless you want to set a specific time zone in the conversion, not the current time zone of your session:
ALTER TABLE tbl ALTER ts_col TYPE timestamptz USING ts_col AT TIME ZONE 'Europe/London';
Be sure to use a time zone name in this case, not a simple offset nor an abbreviation. Details:
- Time zone names with identical properties yield different result when applied to timestamp
- Ignoring timezones altogether in Rails and PostgreSQL
The documentation:
ALTER TABLE
changes the definition of an existing table. There are several subforms described below. Note that the lock level required may differ for each subform. AnACCESS EXCLUSIVE
lock is held unless explicitly noted.
ALTER column_name TYPE data_type
takes such an ACCESS EXCLUSIVE
lock. While the internal storage format of timestamp
and timestamptz
are identical, the internal value is typically changed by the conversion (depending on the time zone setting of the session!). Postgres has to write a new version of every row in the table, so this requires a table rewrite as well. Since the operation took an ACCESS EXCLUSIVE
lock there is no need to keep old row versions and you will see no dead tuples after the conversion.
SQL Fiddle demonstrating the role of the time zone setting on the conversion. I also added an example converting varchar
to text
, which does not require a table rewrite - unless you move to a shorter length modifier.
Note how I cast the output to text
(ts_col::text
) to keep the JDBC layer in sqlfiddle from adding some more (generally unwanted!) representation magic.
Concurrent transactions trying to access the table after your transaction has started will wait until this transaction has finished.
You could try to keep the lock time short by preparing a new table in the background, delete the old table and rename the new, but this will make concurrent transactions fail with an error like:
ERROR: could not open relation with OID 123456
Details:
- Best way to populate a new column in a large table?
"Similar cases" for timestamp
/ timestamptz
Like varchar
or numeric
timestamp, time and interval types allow modifiers. For example, timestamps store up to 6 digits for fractional seconds per default, but you can modify that: timestamp(0)
does not store fractional seconds.
The conversion from varchar(10)
-> varchar(20)
does not requires a table rewrite because values in the source type are guaranteed to fit (binary compatible) in the target type as well.
The same is true for timestamp (0)
-> timestamp
or timestamptz(3)
-> timestamptz(5)
. That's what the manual refers to in the quoted passage in the release notes:
Table rewrites are also avoided in similar cases involving the
interval
,timestamp
, andtimestamptz
types.
来源:https://stackoverflow.com/questions/32480700/will-changing-column-from-timestamp-to-timestamptz-lock-the-table