有人在Go语言中文网微信群里提出了这样一个问题,如下图(文档地址:https://dev.mysql.com/doc/internals/en/date-and-time-data-type-representation.html)不明白为什么DATE要用YYYY×16×32+MM×32+DD表示,主要是为什么16和32。我解释了一下,但是他似乎还是不明白。我干脆写一篇文章详细解释一下,希望能帮到不清楚的人,答案不是重点,关键在于分析的过程。01说一件真实的事情。几年前,一个大厂的iOS开发人员说,之前接触过PHP,知道一些后端,所以简单聊了一下(半面试形式)。讲到的一些点让我觉得他的基础很薄弱,于是试探性地问:int类型一般占用多少空间?(我一般不问这种问题,以免让人觉得“看不起”他)他回答:32。我知道他对这篇文章感到困惑。所以我问:单位是什么?他不确定地回答:是一个字节吗?!回到MySQL中的问题,DATE是以3字节整数类型存储的,如何存储呢?如果不考虑空格,DATE类型是最简单的存储方式可能是直接将YYYYMMDD作为一个整数存储,例如:20210128,这个数字可以用3个字节存储吗?1字节(byte)有8位(bit),3字节有24位。如果表示无符号整数,最多可以表示(2<<24)-1(移位操作的优先级低于减法),即16777215。显然小于20210128,所以我们不能直接用YYYYMMDD作为整数贮存。如果直接把YYYYMMDD这种形式看成一个整数,中间会出现很多“坑”,即很多数字没有用到,不连续,空间利用率太低。比如下图中,20210101第一天比20201231大了8870。因此,我们可以采用“压缩”的方式,让日期集合更加“紧凑”。这就是MySQL在这里采用的方法。具体为什么是YYYY×16×32+MM×32+DD?02日期DD的范围是:1~31,可以用5位数字表示。月份的范围是:1~12,可以用4位表示,但是前5位被DD占用,所以MM必须从第6位开始,所以需要左移(<<)5位数,也就是乘以32,所以MM×32就是这样来的。这样MM和DD一共占用5+4=9位,3个字节还剩15位(24位),也就是说15位可以存储年份(YYYY),15位可以表示长达32767年,远超2021年。因此,对于YYYY,需要左移5+4位,即YYYY×32×16。至于TIME,因为用秒表示的值可以用3个字节存储,所以直接将TIME转为秒。即DD×24×3600+HH×3600+MM×60+SS,DATETIME用8个字节存储,4个字节存储日期,4个字节存储时间。因为空间比较充足,所以这里的“压缩”没有使用二进制位,而是直接使用十进制。YYYY×10000+MM×100+DD,年月日不会重叠,没有4个字节可以表示的范围。时间是一样的。本文转载自微信?「polarisxu」,可通过以下二维码关注。转载本文请联系polarisxu公众号
