问题 1/3:
我有许多 MySQL
数据库要连接并希望确保跨查询的时间一致性。因此,例如,其中一台服务器当前处于 CDT
时区。
> select CURRENT_TIMESTAMP, @@system_time_zone, @@global.time_zone, @@session.time_zone;
+---------------------+--------------------+--------------------+---------------------+
| CURRENT_TIMESTAMP | @@system_time_zone | @@global.time_zone | @@session.time_zone |
+---------------------+--------------------+--------------------+---------------------+
| 2019-05-31 09:44:45 | CDT | SYSTEM | SYSTEM |
+---------------------+--------------------+--------------------+---------------------+
注意:我们现在处于 DST
,因此是 CDT
。我假设这会在 DST
之外时自动更改为 CST
,对吗?
根据以上知识,我的 DSN
后缀看起来像这样:
...?parseTime=true&loc=America%2FChicago // i.e. 'America/Chicago' - maybe 'CST6CDT' would work too?
那么,go
中是否有一种编程方式可以将 3 字母代码 CDT
映射到更正式的时区名称,如 America/Chicago
.
2/3:
上面是先有鸡还是先有蛋的场景:为了确定远程服务器的时区,需要连接/查询服务器;有了这些知识,DSN 参数可能会因将来的调用而改变。
事后是否可以更改 DSN 参数,或者是否需要创建一个全新的连接 sql.DB
连接池?
3/3:
您可能会问,为什么要检查时区是否已更改 - 它不是静态的吗?在负载平衡的数据库情况下应该做什么?理论上,两个副本可以位于不同的时区吗?
所有带有时间戳的列是否都应该用 SQL UNIX_TIMESTAMP()
包装起来以规范化数据并避免这种令人头疼的问题?
我也可以进入时间漂移,但我现在就到此为止。
最佳答案
1/3
go
使用 IANA 的 Time Zone Database具有精确的区域名称。
试图逆向工程 MySQL 如何从 (Linux) 主机确定本地时区格式并在 go
客户端中复制该逻辑 - 正如@MattJohnson 指出的那样 - 被证明是不可靠的。
2/3
database/sql.DB
- 通过 Open(drv, DSN)
创建 - 将对所有连接使用相同的 DSN
。虽然 sql.DB
旨在创建一次并多次使用 - 但事后无法更改 DSN
- 因此需要创建一个品牌更改 DSN
时新建 sql.DB
。
3/3
因此,更好的策略似乎是利用 MySQL
将所有 datetime
值在传输到客户端之前从本地时区转换为 UTC 时区。这消除了在连接时通过 DSN
设置数据库(可能未知)时区的复杂性。
一个有前途的选择是设置连接的 session 时区:
SET @@session.time_zone = "+00:00";
- 但是,这仅适用于当前 连接(在连接池中)。然而,
go
客户端在任何给定时间都不知道他们可能正在使用哪个空闲连接。 - 因此,为确保它始终有效,需要在所有查询 之前手动应用它。即使只有一个数据库连接在使用中 - 如果连接失败并且连接重试开始 - 之前的任何 session 状态都将丢失。
因此,将所有 datatime
列包装成一个转换函数,如下所示:
CONVERT_TZ(`STAMP_UPDATED`,@@session.time_zone,'+00:00')
确保时区计算在查询时完成,并且不会在连接重新连接等期间丢失。
所以现在 DSN
不再需要指定 loc
- 因为 UTC
是默认值。事实上 DSN
只需要 ?parseTime=true
的后缀选项就可以让 datetime
被翻译成 go
的原生 time.Time
。
最后也是最重要的一点,这将适用于设置为任何时区的任何服务器。
H/T answer .
关于mysql - 与 SQL 的时区协调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56397882/