zoukankan      html  css  js  c++  java
  • 从 Oracle 到 PostgreSQL :从 Uptime 到数据库实例运行时间

    在接触操作系统时,我们常常习惯通过 uptime 来看看系统的启动运行时间,例如:

    [oracle@zData ~]$uptime	
    17:00:17 up 656 days, 22:18,  4 users, load average: 0.16, 0.16, 0.14


    640?wx_fmt=png


    在 Oracle 数据库中,同样类似的,可以计算出数据库的启动时间,以了解数据库实例连续运行的时间。以下的 SQL 查询,通过时间运算得出了数据库的相关运行时间值:

    SQL> COLUMN STARTED_SINCE format A25	
    SQL> COLUMN UPTIME format A50	
    SQL> SELECT TO_CHAR (startup_time, 'DD-MON-YYYY HH24:MI:SS')started_since,	
    2         TRUNC (SYSDATE -(startup_time))	
    3         || ' day(s), ' || TRUNC (  24 * ((SYSDATE - startup_time) -	
    4         TRUNC (SYSDATE - startup_time)))	
    5         || ' hour(s), '|| MOD (TRUNC (  1440 * ( (SYSDATE - startup_time) -	
    6         TRUNC (SYSDATE - startup_time))),60)	
    7         || ' minute(s), '|| MOD (TRUNC (  86400 * ( (SYSDATE - startup_time) -	
    8         TRUNC (SYSDATE - startup_time))),60)	
    9         || ' seconds' uptime	
    10 FROM v$instance;	
     	
    STARTED_SINCE              UPTIME	
    ---------------------------------------------------------------------------	
    08-DEC-2018 21:36:19     164 day(s), 19 hour(s), 19 minute(s), 13seconds


    640?wx_fmt=jpeg


    在 PostgreSQL 中,同样可以通过查询得到类似的效果:

    select pg_postmaster_start_time()as START_SINCE,	
    date_part('day',current_timestamp-pg_postmaster_start_time())||'day(s),'||	
    date_part('hour',current_timestamp-pg_postmaster_start_time())||'hour(s),'||	
    date_part('minutes',current_timestamp-pg_postmaster_start_time())||'minute(s),'||	
    date_part('seconds',current_timestamp-pg_postmaster_start_time())||'second(s)' as UPTIME;	
              start_since          |                       uptime                        	
    -------------------------------+-----------------------------------------------------	
     2019-04-25 18:13:25.968474+08 | 26day(s),23 hour(s),4 minute(s),1.279786 second(s)


    在 PostgreSQL 中,关于时间处理的两个函数非常有用,date_part 可以将日期中的不同部分抽取出来,而 date_trunc 则类似 Oracle 中 Trunc 函数的作用,将时间进行截取处理。

    select date_trunc('day',current_timestamp-pg_postmaster_start_time());	
     date_trunc 	
    ------------	
     26 days	
    (1 row)	
    eygle=# selectdate_trunc('hour',current_timestamp-pg_postmaster_start_time());	
        date_trunc    	
    ------------------	
     26 days 22:00:00	
    (1 row)


    也可以通过 extract 实现类似的功能:

    selectextract(day from current_timestamp-pg_postmaster_start_time());	
     date_part 	
    -----------	
            26	
    eygle=# select extract(hour fromcurrent_timestamp-pg_postmaster_start_time());	
     date_part 	
    -----------	
            23


    PostgreSQL 中,用户返回当前时间的函数有 current_date、current_time 和 current_timestamp 等:

    select current_timestamp;	
           current_timestamp       	
    -------------------------------	
     2019-05-22 17:10:40.575532+08	
    (1 row)	
     	
    eygle=# select current_date;	
     current_date 	
    --------------	
     2019-05-22	
    (1 row)


    这和 Oracle 数据库非常相似,通过 sysdate 和 systimestamp 能够返回 Oracle 的当前时间,以下是 Oracle 数据库中的语法:

    SQL> select sysdate,systimestamp from dual;	
     	
    SYSDATE	
    -------------------	
    SYSTIMESTAMP	
    ---------------------------------------------------------------------------	
    2019-05-22 17:25:47	
    22-MAY-19 05.25.47.109129 PM +08:00


    注意,dual 表是Oracle中的特殊存在,而 PostgreSQL 的函数不需要这样的依托直接返回了结果。

     

    在 PostgreSQL 中,功能近似的函数特别丰富,例如如下这些函数:

    transaction_timestamp()	
    statement_timestamp()	
    clock_timestamp()	
    timeofday()	
    now()


    此外,通过 interval 可以对时间进行推移:


    select now() + interval '2 years';           ?column?           ------------------------------ 2021-05-22 17:51:13.98532+08 eygle=# select now() + interval '1 month';           ?column?            ------------------------------- 2019-06-22 17:52:12.737686+08(1 row) eygle=# select now() - interval '1 week';           ?column?            ------------------------------- 2019-05-15 17:52:26.425832+08(1 row)eygle=# select now() + '10 min';           ?column?            ------------------------------- 2019-05-22 18:02:35.013766+08(1 row)


    在 PostgreSQL 中还有一个有趣的函数 age,可以用来计算年龄,1990年出生的同学们竟然马上要30岁啦,成家了没,同学们?

    select age(now(),date '1990-01-01');	
                       age                   	
    -----------------------------------------	
     29 years 4 mons 21 days17:58:43.875068


    在计算机系统中,还有一个特殊的时间计算方法,叫做 Unix Time,这个时间是自 UTC 时间 1970-01-01 00:00:00至今的秒数,这个计时方式同样被传导到数据库中。(如下图所示)


    640?wx_fmt=jpeg


    在 PostgreSQL 中,可以通过 epoch(即特定时点 1970-01-01 00:00:00 UTC)为起点进行计算。以下是两个方向的转换方式:

    select extract(epoch from now());	
        date_part     	
    ------------------	
     1558519237.02995	
     	
    eygle=# SELECT TIMESTAMP WITH TIME ZONE 'epoch' + 1558519237 * INTERVAL '1second';	
            ?column?        	
    ------------------------	
     2019-05-22 18:00:37+08


    640?wx_fmt=png


    在 MySQL 中,通过 FROM_UNIXTIME和 UNIX_TIMESTAMP 函数可以实现类似的转换和计算:

    mysql> select FROM_UNIXTIME(1558519237,'%Y-%m-%d %H:%i:%S');	
    +-----------------------------------------------+	
    | FROM_UNIXTIME(1558519237,'%Y-%m-%d %H:%i:%S') |	
    +-----------------------------------------------+	
    | 2019-05-22 18:00:37                           |	
    +-----------------------------------------------+	
    1 row in set (0.00 sec)	
     	
    mysql> select UNIX_TIMESTAMP('2019-05-22 18:00:37');	
    +---------------------------------------+	
    | UNIX_TIMESTAMP('2019-05-22 18:00:37') |	
    +---------------------------------------+	
    |                           1558519237 |	
    +---------------------------------------+	
    1 row in set (0.02 sec)


    在 Oracle 的数据库中,UnixTime 同样是非常重要的,在 SYS 用户的 SMON_SCN_TIME字典中记录中 Unix Time 和 Date 时间的对应,TIME_MP 和 TIME_DP 两个字段记录的就是这样的信息,这些信息在恢复时非常重要,Oracle 又将时间和 SCN 关联了起来。

    SQL> desc smon_scn_time	
    Name                         Null?   Type	
    ------------------------------------------------- ----------------------------	
    THREAD                        NUMBER	
    TIME_MP                       NUMBER	
    TIME_DP                       DATE	
    SCN_WRP                       NUMBER	
    SCN_BAS                       NUMBER	
    NUM_MAPPINGS                       NUMBER	
    TIM_SCN_MAP                       RAW(1200)	
    SCN                                NUMBER	
    ORIG_THREAD                        NUMBER	
     	
    SQL> select time_mp from smon_scn_time where rownum < 2;	
    TIME_MP	
    ----------	
    1558502931	
     	
    SQL> select time_mp,time_dp from smon_scn_time where rownum < 2;	
    TIME_MP TIME_DP	
    ---------- -------------------	
    1558502931 2019-05-22 05:28:51	
     	
    SQL>  select time_mp,time_dp fromsmon_scn_time	
    2 where time_mp = (select max(time_mp) from smon_scn_time);	
     	
    TIME_MP TIME_DP	
    ---------- -------------------	
    1558519988 2019-05-22 10:13:08


    Oracle 数据库中没有提供转换函数,我们通过 PostgreSQL 转换一下验证:

    select TIMESTAMP WITH TIME ZONE 'epoch' + 1558519988 * INTERVAL '1second';	
            ?column?        	
    ------------------------	
     2019-05-22 18:13:08+08


    注意到转换的时间和 Oracle 记录的 TIME_DP相差了 8 个小时,这是什么原因呢?这是因为数据库操作系统采用的是 CST 时间:

    [oracle@zData ~]$ date	
    Wed May 22 18:27:35 CST 2019


    CST 时间和 UTC 时间相差 8 小时(CST = UTC + 8),smon_scn_time 记录的时间按照 CST 时间进行了换算,实际上是非常精确的吻合。


    在 Oracle 数据库中,还有一个动态性能视图 V$TIMER 记录了 epoch 时间,官方文档这样描述(来自 19c 文档):


    V$TIMER displays the elapsed time in hundredths of a second. Time ismeasured since the beginning of the epoch, which is operating system specific,and wraps around to 0 again whenever the value overflows four bytes (roughly497 days).


    这段描述说明 V$TIMER 记录的是厘秒,从 epoch 时间起点量度,这个值来自操作系统,由于在数据库中使用 4 bytes 记录,当主机连续运行大约 497 天之后,这个值会归零重新开始。在 Oracle 9i 中,因为 JOB 的时间定义依赖这个值,所以存在一个 BUG 是 497 天后所有 JOB 会停止执行。


    多年以前遇到过一个有趣的故事,在这里引用一下。


    某日,同事告诉我一个发现,他说一台数据库的运行时间超过了操作系统的启动时间。


    从数据库内部可以查询到数据库实例的启动时间:

    SQL> SELECT TO_CHAR(startup_time, 'DD-MON-YYYY HH24:MI:SS') started_at,	
    2            TRUNC (SYSDATE -(startup_time))	
    3        || ' day(s), ' || TRUNC (  24 *((SYSDATE - startup_time) - 	
    4        TRUNC (SYSDATE - startup_time)))	
    5        || ' hour(s), '|| MOD (TRUNC (  1440 *(  (SYSDATE - startup_time) - 	
    6        TRUNC (SYSDATE - startup_time))),60)	
    7        || ' minute(s), '|| MOD (TRUNC ( 86400 * (  (SYSDATE - startup_time) - 	
    8        TRUNC (SYSDATE - startup_time))),60)	
    9        || ' seconds' uptime	
    10  FROM v$instance;	
    	
    STARTED_AT                UPTIME	
    ------------------------- --------------------------------------------------	
    05-JUL-2005 10:36:58      803 day(s), 2 hour(s), 27 minute(s),55 seconds


    从这里看数据库实例启动了 803 天左右,也就是说自 2005-07-05 开始这个数据库一直在不间断的运行着。而从操作系统的 uptime 来看,系统不过启动了 306 天:

    SQL> ! uptime	
    13:06:21  up 306 days, 19:00,  1 user,  load average: 0.00,0.00, 0.00


    同事问我原因,首先我们检查 alert 文件,发现数据库的确是 2005 年启动的。再研究一下,发现这是又一次时间溢出的问题,

    由于某些 Linux 内核使用 32 位无符号长整型来计算时间,32 位的最大值就是 0xffffffff,再加 1 就将溢出变为 0。


    以下一小段 C 代码可以解释这种溢出:

    [root@jumper root]# cat a.c	
    int main(void){	
    unsigned int num = 0xffffffff;	
    	
    printf("num is %d bits long
    ", sizeof(num) * 8);	
    printf("num = 0x%x
    ", num);	
    printf("num + 1 = 0x%x
    ", num + 1);	
    	
    return 0;	
    }	
    [root@jumper root]# gcc -o un a.c	
    [root@jumper root]# ./un	
    num is 32 bits long	
    num = 0xffffffff	
    num + 1 = 0x0


    在这个 Linux 发行版本上,这个时间就此溢出:

    SQL> ! uname -a	
    Linux moto 2.4.21-15.ELsmp #1 SMP Thu Apr 22 00:18:24 EDT 2004 i686 i686 i386GNU/Linux	
    SQL> ! cat /etc/redhat-release	
    Red Hat Enterprise Linux AS release 3 (Taroon Update 2)


    根据 497 天再来计算一下:

    SQL> select 803 - 306 from dual;803-306----------497


    当前数据库的显示是正确的,803 天减去 uptime 显示时间,得出的正好是 497 天。


    关于时间,Oracle 中有很多有意思的话题,参考:

    https://www.eygle.com/archives/2007/09/497_day_linux_limit.html

    https://www.eygle.com/archives/2004/11/job_can_not_execute_auto.html


    云和恩墨大讲堂PostgreSQL社群成立啦,欢迎加入,扫描图片二维码即可。

    640?wx_fmt=png

    640?wx_fmt=jpeg

    公司简介  | 招聘 | DTCC | 数据技术嘉年华 | 免费课程 | 入驻华为严选商城

      640?wx_fmt=jpeg

    zCloud | SQM | Bethune Pro2 zData一体机 | Mydata一体机 | ZDBM 备份一体机

    640?wx_fmt=jpeg

    Oracle技术架构 | 免费课程 | 数据库排行榜 | DBASK问题集萃 | 技术通讯 

    640?wx_fmt=jpeg

    升级迁移 | 性能优化 | 智能整合 安全保障 |  架构设计 | SQL审核 | 分布式架构 | 高可用容灾 | 运维代维

    云和恩墨大讲堂 | 一个分享交流的地方

    长按,识别二维码,加入万人交流社群


    640?wx_fmt=jpeg

    请备注:云和恩墨大讲堂

  • 相关阅读:
    Maven入门教程
    认识Java Core和Heap Dump
    [Java IO]03_字符流
    Eclipse 实用技巧
    可变和不可变的区分
    什么猴子补丁待补充
    当退出python时,是否释放全部内存
    解释python中的help()和dir()函数
    在python中是如何管理内存的
    解释一下python中的继承
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13312058.html
Copyright © 2011-2022 走看看