【mysql案例】mysql 5.7日志文件里面时间的时区不对的问题

问题:

新安装的MySQL 5.7.27,发现error log、general log日志里面日志时间的时区不对。

error log

2019-09-11T12:53:23.988658Z 0 [Warning] CA certificate ca.pem is self signed.
2019-09-11T12:53:24.007777Z 0 [Note] Server hostname (bind-address): '*'; port: 3306
2019-09-11T12:53:24.008117Z 0 [Note] IPv6 is available.
2019-09-11T12:53:24.008148Z 0 [Note]   - '::' resolves to '::';
2019-09-11T12:53:24.008226Z 0 [Note] Server socket created on IP: '::'.
2019-09-11T12:53:24.088216Z 0 [Note] /usr/local/mysql/libexec/mysqld: ready for connections.
Version: '5.7.27-debug-log'  socket: '/usr/local/mysql/tmp/mysql.sock'  port: 3306  Source distribution

general log

2019-09-11T12:56:32.547294Z	    2 Query	show global variables like 'log_timestamps'
2019-09-11T13:01:04.907420Z	    2 Query	select DATABASE(), USER() limit 1
2019-09-11T13:01:04.908582Z	    2 Query	select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1
2019-09-11T13:01:04.908974Z	    2 Statistics

原因:

mysql 5.7.2新增了参数 log_timestamps,用于控制error log、general log、slow log日志记录使用的时区,只影响写入文件的时区,不影响写入表中记录的时区。默认值是UTC,支持设置为SYSTEM使用系统的时区。

• log_timestamps

Introduced

5.7.2

Command-Line Format

--log_timestamps=#

System Variable

Name

log_timestamps

Variable Scope

Global

DynamicYes Variable

Permitted Values

Type

enumeration

Default

UTC

Valid Values

UTC

SYSTEM

This variable controls the timestamp time zone of error log messages, and of general query log and slow query log messages written to files. It does not affect the time zone of general query log and slow query log messages written to tables (mysql.general_log, mysql.slow_log). Rows retrieved from those tables can be converted from the local system time zone to any desired time zone with CONVERT_TZ() or by setting the session time_zone system variable.

Permitted log_timestamps values are UTC (the default) and SYSTEM (local system time zone). Timestamps are written using ISO 8601 / RFC 3339 format: YYYY-MM-DDThh:mm:ss.uuuuuu plus a tail value of Z signifying Zulu time (UTC) or ±hh:mm (an offset from UTC).

This variable was added in MySQL 5.7.2. Before 5.7.2, timestamps in log messages were written using the local system time zone by default, not UTC. If you want the previous log message time zone default, set log_timestamps=SYSTEM.

 

修复:

1)执行 set global log_timestamps=SYSTEM

    执行之后,新的日志的时区就会变成系统默认的时区。

2)在my.cnf中添加 log_timestamps=SYSTEM

<think>嗯,用户想在MySQL中按照每四个小时的时间间隔对创建时间进行分组,并统计每个时间段内的记录数。首先,我需要确定如何处理时间分组。通常,使用GROUP BY来分组,但时间间隔是每四个小时,这可能需要对时间进行计算。 首先,考虑如何将时间戳转换为四个小时的区间。可能需要用数学函数来处理。比如,使用HOUR函数获取创建时间的小时部分,然后用除法取整,比如HOUR(created_at) DIV 4,这样0-3小时是0,4-7是1,依此类推。然后乘以4得到每个区间的起始小时。例如,(HOUR(created_at) DIV 4) * 4可以得到每个区间的起始小时数。 不过,这样可能只处理小时部分,而忽略了日期。如果数据跨越多天,需要将日期和小时结合起来。可能需要使用DATE_FORMAT函数来提取日期部分,然后结合处理后的四小时区间。例如,DATE(created_at)可以获取日期,然后和四小时区间组合成时间段。 接下来,构造分组字段。例如,可以将日期和四小时区间拼接起来,形成如'2023-10-01 00-04'这样的格式。可以使用CONCAT函数,将DATE(created_at)和计算出的区间起始小时组合,并加上间隔。例如,CONCAT(DATE(created_at), ' ', (HOUR(created_at) DIV 4)*4, '-', (HOUR(created_at) DIV 4)*4 +4)。 不过,这样拼接的时间段可能不会显示正确的时间格式,比如00-04,04-08等。可能需要使用LPAD函数来保证小时数是两位数的格式,避免像0-4变成0-4而不是00-04。例如,LPAD((HOUR(created_at) DIV 4)*4, 2, '0')来确保两位数。 然后,在SELECT子句中,将上述表达式作为时间段,并使用COUNT(*)统计每个组的记录数。最后,用GROUP BY这个时间段表达式。 不过,这样处理可能存在一个问题:如果created_at的时间跨天,比如23点到第二天的3点,这时候DIV 4得到的区间会是5(23 DIV4=5,因为23/4=5.75,DIV取整除结果是5),但乘以4得到20,所以区间是20-24,但实际时间可能到第二天的00-04。这时候,日期部分需要调整,比如当小时数超过23时,日期应该加一天,但这种情况可能需要更复杂的处理。不过,HOUR函数返回的是0-23的值,所以当小时是23时,DIV4是5,乘以4得到20,所以区间是20-24,但24点即第二天的0点,这时候日期部分应该还是当天的日期,这会导致分组错误。例如,时间2023-10-01 23:30:00会被分到20-24区间,但实际应该属于当天的20-24,而第二天的00-04属于新的日期。所以,这种情况下,可能需要将时间转换为从当天的0点开始计算,或者考虑使用UNIX时间戳进行更精确的计算。 或者,使用TIMESTAMP函数和数学运算。例如,将时间转换为从某个起点开始的四小时间隔。比如,计算时间戳除以四小时的秒数(4*3600=14400),然后取整数部分,再乘以14400得到每个区间的起始时间戳。这可能更准确,尤其是跨天的情况。例如: SELECT FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(created_at)/14400)*14400) AS time_interval, COUNT(*) AS total FROM table_name GROUP BY time_interval ORDER BY time_interval; 这样,每个四小时的间隔会被计算为时间戳的整数倍,然后转换为日期时间格式。这方法可能更精确,因为考虑了日期和时间的连续性,而不仅仅是小时部分。例如,2023-10-01 23:30:00的时间戳除以14400后取整,再乘以14400会得到该四小时区间的起始时间戳,可能是当天的20:00:00或次日的00:00:00,取决于具体时间。不过需要测试这个方法是否正确。 另外,需要考虑时区问题,UNIX_TIMESTAMP可能受MySQL服务器的时区设置影响。如果created_at存储的是带时区时间,可能需要调整。 不过,用户的需求可能更倾向于按自然日的四小时分段,比如每天分成六个时间段:00-04, 04-08,...,20-24。这时候,使用HOUR DIV4的方法可能更合适,但需要处理跨天的案例。例如,如果有一个时间是2023-10-01 23:59:59,它属于当天的20-24区间,而2023-10-02 00:00:00属于新的日期的00-04区间。这样,每个日期内的四小时分组是正确的,但可能需要将日期与小时区间结合起来。 所以,最终的SQL可能需要结合日期和四小时区间,例如: SELECT CONCAT(DATE(created_at), ' ', LPAD((HOUR(created_at) DIV 4) * 4, 2, '0'), ':00-', LPAD((HOUR(created_at) DIV 4) * 4 + 4, 2, '0'), ':00') AS time_interval, COUNT(*) AS total FROM table_name GROUP BY DATE(created_at), (HOUR(created_at) DIV 4) ORDER BY DATE(created_at), (HOUR(created_at) DIV 4); 这里,分组依据是日期和四小时区间的编号(0-5),这样可以确保同一天内的小时正确分组,并且跨天的数据不会混淆。同时,显示的时间区间会是当天的00-04,04-08等。 另一个方法是使用DATE_FORMAT来格式化时间,例如: SELECT DATE_FORMAT(created_at, '%Y-%m-%d') AS date, CONCAT(LPAD(FLOOR(HOUR(created_at)/4)*4, 2, '0'), ':00-', LPAD(FLOOR(HOUR(created_at)/4)*4 +4, 2, '0'), ':00') AS interval, COUNT(*) FROM table_name GROUP BY date, FLOOR(HOUR(created_at)/4); 这可能更简洁,同时确保分组正确。 测试这两种方法哪种更有效。例如,假设created_at是'2023-10-01 03:00:00',HOUR是3,DIV4是0,乘以4得到0,区间是00-04。对于'2023-10-01 05:00:00',DIV4是1(5/4=1.25,DIV取整数部分),所以区间是04-08。这样分组是正确的。 另外,需要考虑是否要包括日期部分在分组中,否则不同日期的同一时间段会被合并。例如,2023-10-01的00-04和2023-10-02的00-04会被分到同一组,这可能不符合用户的需求。所以,分组必须包括日期和小时区间两部分。 总结,正确的SQL应该将日期和四小时区间作为分组条件,并格式化输出时间段。因此,最终的SQL可能类似于: SELECT CONCAT(DATE(created_at), ' ', LPAD((HOUR(created_at) DIV 4) * 4, 2, '0'), ':00-', LPAD((HOUR(created_at) DIV 4) * 4 +4, 2, '0'), ':00') AS time_interval, COUNT(*) AS total FROM your_table GROUP BY DATE(created_at), HOUR(created_at) DIV 4 ORDER BY time_interval; 这样,每个时间段会按日期和四小时区间正确分组,并且显示为如“2023-10-01 00:00-04:00”的格式。 但用户可能需要更简洁的时间段表示,比如只显示小时部分,如“00-04”,但如果数据跨天,这样不同日期的同一时间段会被合并,这可能不符合需求。所以,必须包含日期部分。 另外,如果用户希望时间段不依赖于日期,比如所有数据中的00-04、04-08等,不管日期如何,这可能不太合理,但用户的问题中明确提到“按照每四个小时”分组,可能是指每个自然日的四个小时段。因此,必须将日期包含在内。 最后,确保在分组时使用正确的表达式,避免歧义。在GROUP BY中,可以直接使用DATE(created_at)和HOUR(created_at) DIV4,而不是别名,因为某些SQL版本可能不允许使用别名。 现在,检查是否存在错误,比如当HOUR(created_at) DIV4为5时,乘以4得到20,加上4就是24,但小时的范围是0-23,所以可能需要处理24的情况,比如显示为20-24,而24点即次日的0点。这在显示上是合理的,因为用户通常能理解20-24指的是当天20点到次日0点吗?或者可能更希望显示为20-00?但通常写成20-24会更明确。或者,可能更准确的是写成20:00-00:00,但需要测试。 例如,当HOUR是23时,DIV4是5,乘以4得到20,所以区间是20-24,但实际是20:00到24:00,也就是次日的0点。这可能需要在显示时处理,比如将24改为00,并调整日期部分。例如,如果小时区间是20-24,那么当时间到达24时,日期应该加一天,但这样处理可能比较复杂。不过,根据用户的需求,可能只需要显示为当天的20-24,而实际的时间范围是包含到当天的23:59:59,而次日的00:00:00属于下一个区间的开始。因此,显示为20-24可能更合理,而不必调整日期。 综上,最终的SQL应该可以正确分组,并按日期和四小时区间显示时间段。</think>在MySQL中,要按每4小时分组统计记录数,可以通过以下步骤实现: ### 1. 使用时间数学运算 通过提取小时数进行除法取整,配合日期函数实现分组: ```sql SELECT CONCAT(DATE(created_at), ' ', LPAD((HOUR(created_at) DIV 4)*4, 2, '0'), ':00-', LPAD((HOUR(created_at) DIV 4)*4+4, 2, '0'), ':00') AS time_interval, COUNT(*) AS total FROM your_table GROUP BY DATE(created_at), HOUR(created_at) DIV 4 ORDER BY created_at; ``` ### 2. 核心原理 - `HOUR(created_at) DIV 4`:将小时数0-23划分为6个区间(0-3=0,4-7=1,...,20-23=5) - `LPAD(...)`:保证时间格式统一为两位数(如00-04) - `DATE(created_at)`:分离日期部分避免跨天混淆 - 分组依据为`日期`+`四小时区间编号`的组合[^1] ### 3. 示例输出 | time_interval | total | |-----------------------|-------| | 2023-10-01 00:00-04:00 | 15 | | 2023-10-01 04:00-08:00 | 32 | ### 4. 跨天特殊处理 对于需要严格时间窗口的场景(如UTC时间),改用时间戳计算: ```sql SELECT FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(created_at)/14400)*14400) AS interval_start, COUNT(*) AS total FROM your_table GROUP BY FLOOR(UNIX_TIMESTAMP(created_at)/14400) ``` (14400秒=4小时,直接按时间戳分桶)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值