@TOC
水平拓展和垂直拓展
简介:
- 当一个开发人员提升计算机系统负荷时,通常会考虑两种方式垂直扩展和水平扩展。选用哪种策略主要依赖于要解决的问题 以及系统资源的限制。
- 如果你已经有一个软件系统需要不断成长,那么你将有意或者无意中选择这两种策略中的一种。
横向扩展也叫水平扩展,用更多的节点支撑更大量的请求。 如一群人搬一个石头纵向扩展又叫垂直扩展,扩展一个点的能力支撑更大的请求。如一个肌肉男搬一个石头
扩展你的web数据库:
- 通过对水平扩展和垂直扩展的基本了解,下面让我们来关注web系统的扩展。一个网站通常有很多组件都需要去考虑它们的扩展性,但是我通常喜欢关注处在最边缘的一个:数据库。
- 为什么数据库是最边缘的?因为数据库通常是共享资源,是几乎所有请求最终的连接点。
你的系统是什么类型的?
- 在 扩展你的数据库时,你必须要问的一个重要问题是:“我所面对的系统是什么类型的?”你所面对的是一个读操作多还是写操作多的系统?
- 读操作多的网站一般包括:在线商城,在商城里用户大部时间是在浏览(读操作),只有少数时间在付款(写操作)、或者博客,在博客上人们大部分时间是在浏览博文(读操作),只有少数时间是在评论或者发表博文(写操作)。
- 相反的,关于写操作非常多的很好的例子包括:信用卡交易处理器,这个系统的主要负载时在处理记录交易(写操作),偶尔会查找交易(读操作)、或者Google分析,主要工作实在记录业务数据(写操作),偶尔会展示分析图(读操作)。
- 了解你所创建的网站是什么类型的,可以在网站成长过程中帮助你选择正确的技术。
读操作扩展:
- 如果你的系统
读操作非常多,那么通过关系型数据库如mysql或者PostgreSql来垂直扩展数据存储是一个不错的选择。 - 结合你的关系型数据库通过使用memcached或者CDN来构建一个健壮的缓存系统,那么你的系统将非常容易扩展。在这种模式中,如果数据库超负荷运行,那么将更多的数据放入缓存中来缓解系统的读压力。当没有更多的数据往缓存中放时,可以更换更快的数据存储硬件或者买更多核的处理器来获取更多的运行通道。摩尔定律使通过这种方法来垂直扩展变得和购买更好的硬件一样简单。
写操作扩展:
- 如果你的系统写操作非常多,那么你可能更希望考虑使用可水平扩展的数据存储方式,比如Riak,Cassandra或者HBase。和大多数关系型数据管理系统不同,这种数据存储随着增长增加更多的节点。由于你的系统大部分时间是在写入,所以缓存曾并不能像在读操作比较频繁的系统中起到那么大作用。
- 很多写频繁的系统一开始使用垂直扩展的方式,但是很快发现并不能根本解决问题。为什么?因为硬盘数和处理器数在某一点达到平衡,在这个边界上再增加一个处理器或者一个硬盘都会是每秒钟的I/O操作数成指数性增长。相反,如果对写频繁的系统采取水平扩展策略,那么你将达到一个拐点,在这个拐点之后如果在增加一个节点都远比使用更多的硬盘来的实惠。
单机,集群,分布式
单机结构:
- 我想大家最熟悉的就是单机结构,一个系统业务量很小的时候所有的代码都放在一个项目中就好了,然后这个项目部署在一台服务器上就好了。
- 整个项目所有的服务都由这台服务器提供。这就是单机结构。
- 那么,单机结构有啥缺点呢?我想缺点是显而易见的,单机的处理能力毕竟是有限的,当你的业务增长到一定程度的时候,单机的硬件资源将无法满足你的业务需求。此时便出现了集群模式,往下接着看。
集群结构:
- 单机处理到达瓶颈的时候,你就把单机复制几份,这样就构成了一个“集群”。集群中每台服务器就叫做这个集群的一个“节点”,所有节点构成了一个集群。
- 每个节点都提供相同的服务,那么这样系统的处理能力就相当于提升了好几倍(有几个节点就相当于提升了这么多倍)。
- 但问题是用户的请求究竟由哪个节点来处理呢?最好能够让此时此刻负载较小的节点来处理,这样使得每个节点的压力都比较平均。要实现这个功能,就需要在所有节点之前增加一个“调度者”的角色,用户的所有请求都先交给它,然后它根据当前所有节点的负载情况,决定将这个请求交给哪个节点处理。这个“调度者”叫做
负载均衡服务器。 - 集群结构的好处就是系统扩展非常容易。如果随着你们系统业务的发展,当前的系统又支撑不住了,那么给这个集群再增加节点就行了。但是,当你的业务发展到一定程度的时候,你会发现一个问题——无论怎么增加节点,貌似整个集群性能的提升效果并不明显了。这时候,你就需要使用微服务结构了。
单机与集群总结:
- 从单机结构到集群结构,你的代码基本无需要作任何修改,你要做的仅仅是多部署几台服务器,每台服务器上运行相同的代码就行了。但是,当你要从集群结构演进到微服务结构的时候,之前的那套代码就需要发生较大的改动了。所以对于新系统我们建议,系统设计之初就采用微服务架构,这样后期运维的成本更低。
- 但如果一套老系统需要升级成微服务结构的话,那就得对代码大动干戈了。所以,对于老系统而言,究竟是继续保持集群模式,还是升级成微服务架构,这需要架构师深思熟虑、权衡投入产出比。
分布式结构:
- 分布式结构就是将一个完整的系统,按照业务功能,拆分成一个个独立的子系统,在分布式结构中,每个子系统就被称为“服务”。这些子系统能够独立运行在web容器中,它们之间通过
RPC(远程过程调用Remote Procedure Call)方式通信。 - 举个例子,假设需要开发一个在线商城。按照微服务的思想,我们需要按照功能模块拆分成多个独立的服务,如:用户服务、产品服务、订单服务、后台管理服务、数据分析服务等等。这一个个服务都是一个个独立的项目,可以独立运行。如果服务之间有依赖关系,那么通过RPC方式调用。
- 这样的好处有很多:系统之间的耦合度大大降低,可以独立开发、独立部署、独立测试,系统与系统之间的边界非常明确,排错也变得相当容易,开发效率大大提升。系统之间的耦合度降低,从而系统更易于扩展。我们可以针对性地扩展某些服务。假设这个商城要搞一次大促,下单量可能会大大提升,因此我们可以针对性地提升订单系统、产品系统的节点数量,而对于后台管理系统、数据分析系统而言,节点数量维持原有水平即可。服务的复用性更高。比如,当我们将用户系统作为单独的服务后,该公司所有的产品都可以使用该系统作为用户系统,无需重复开发。
总结:
集群是个物理形态,分布式是个工作方式。
为什么不推荐使用外键约束
阿里的JAVA规范中有下面这一条:
- 不得使用外键与级联,一切外键概念必须在应用层解决。
首先明确一点外键并非全然没有优点,如:
- 保证数据的完整性和一致性
- 级联操作方便
- 将数据完整性判断托付给了数据库完成,减少了程序的代码量
但是与此同时,也带来很多缺点,使我们不得不去放弃它:
性能问题①假设一张表名为user_tb。那么这张表里有两个外键字段,指向两张表。那么,每次往user_tb表里插入数据,就必须往两个外键对应的表里查询是否有对应数据。如果交由程序控制,这种查询过程就可以控制在我们手里,可以省略一些不必要的查询过程。但是如果由数据库控制,则是必须要去这两张表里判断。并发问题①在使用外键的情况下,每次修改数据都需要去另外一个表检查数据,需要获取额外的锁。若是在高并发大流量事务场景,使用外键更容易造成死锁。扩展性问题①做平台迁移方便,比如你从Mysql迁移到Oracle,像触发器、外键这种东西,都可以利用框架本身的特性来实现,而不用依赖于数据库本身的特性,做迁移更加方便。 ②分库分表方便,在水平拆分和分库的情况下,外键是无法生效的。将数据间关系的维护,放入应用程序中,为将来的分库分表省去很多的麻烦。技术问题①使用外键,其实将应用程序应该执行的判断逻辑转移到了数据库上。那么这意味着一点,数据库的性能开销变大了,那么这就对DBA(数据库管理员 Database Administrator)的要求就更高了。很多中小型公司由于资金问题,并没有聘用专业的DBA,因此他们会选择不用外键,降低数据库的消耗。 ②相反的,如果该约束逻辑在应用程序中,发现应用服务器性能不够,可以加机器,做水平扩展。如果是在数据库服务器上,数据库服务器会成为性能瓶颈,做水平扩展比较困难。
总结:
- 外键目的是为了保证数据完整性和一致性,避免产生脏数据。
- 设置外键的缺点: ①影响写入性能:对于insert来说,每次都要判断从表的外键列是否在主表中存在(例如每次插入orders表,都要判断下user_id是否在users中存在),会降低数据库的写入性能,对于MySQL本来就只有Master输出写能力的数据库,就不太合适了,MySQL开发规范规定不允许使用外键也是有一定道理的。 ②并发问题:在使用外键的情况下,每次修改数据都需要去另外一个表检查数据,需要获取额外的锁。在高并发大场景,使用外键造成死锁或锁等几率更大。
实际开发中,更多的是不靠外键来保证数据的完整性和一致性,而是通过的业务逻辑,比如用户要下单,必须先登录系统,下单只需要将登录的用户编号写入到订单表,这个用户必然是存在于用户表的,对于一个用户友好的系统来说,尽量让用户选择,不要人工输入,这样可以保证数据一致性,避免脏数据的产生。
自增主键与UUID
自增主键:
- 优点: 1、数据库自动编号,速度快,而且是增量增长,按顺序存放,对于检索非常有利; 2、数字型,占用空间小,易排序,在程序中传递也方便; 3、能够保证独立性,程序可以在不同的数据库间迁移,效果不受影响。 4、保证生成的ID不仅是表独立的,而且是库独立的,这点在你想切分数据库的时候尤为重要。
- 缺点 : 1、因为自动增长,在手动要插入指定ID的记录时会显得麻烦,尤其是当系统与其它系统集成时,需要数据导入时,很难保证原系统的ID不发生主键冲突(前提是老系统也是数字型的)。特别是在新系统上线时,新旧系统并行存在,并且是异库异构的数据库的情况下,需要双向同步时,自增主键将是你的噩梦; 2、在系统集成或割接时,如果新旧系统主键不同是数字型就会导致修改主键数据类型,这也会导致其它有外键关联的表的修改,后果同样很严重; 3、若系统也是数字型的,在导入时,为了区分新老数据,可能想在老数据主键前统一加一个字符标识(例如“o”,old)来表示这是老数据,那么自动增长的数字型又面临一个挑战。 4、如果经常有合并表的操作,就可能会出现主键重复的情况 5、很难处理分布式存储的数据表。数据量特别大时,会导致查询数据库操作变慢。此时需要进行数据库的水平拆分,划分到不同的数据库中,那么当添加数据时,每个表都会自增长,导致主键冲突。
UUID:
- 详解:
①
使用UUID32不用UUID36。
package cn.itcast.demo3;
import java.util.UUID;
/**
* 通过UUID随机生成36位、32位唯一识别码(唯一字符串)
*/
public class Test {
public static void main(String[] args) {
int i = 0;
while(i < 10){
String a = UUID.randomUUID().toString();
System.out.println(a);
System.out.println(a.length());
//public String replaceAll(String regex, String replacement)
//replaceAll() 方法使用给定的参数 replacement 替换字符串所有匹配给定的正则表达式的子字符串
String b = a.replaceAll("-", "");
System.out.println(b);
System.out.println(b.length());
System.out.println("*******************************");
i++;
}
}
}
-----------------------------------输出:-----------------------------
64a61d05-23c5-49d6-88fa-a4fd3e8a940b
36
64a61d0523c549d688faa4fd3e8a940b
32
*******************************
30e94684-1977-4355-8b83-220b31525d49
36
30e94684197743558b83220b31525d49
32
- 优点: 1、能够保证独立性,程序可以在不同的数据库间迁移,效果不受影响。 2、保证生成的ID不仅是表独立的,而且是库独立的,这点在你想切分数据库的时候尤为重要。
- 缺点: 1、比较占地方,和INT类型相比,存储一个UUID要花费更多的空间。 2、使用UUID后,URL显得冗长,不够友好。 3、没有内置的函数获取最新产生的UUID主键。 4、很难记忆。Join操作性能比int要低。 5、 UUID做主键将会添加到表上的其他索引中,因此会降低性能。
一对一、一对多、多对一、多对多关系
简介:
- 不管是一对一、一对多、多对一、多对多关系,都可以用主键或外键进行关联。
关联映射:一对多/多对一:
- 存在最普遍的映射关系,简单来讲就如球员与球队的关系;
- 一对多:从球队角度来说一个球队拥有多个球员 即为一对多
- 多对一:从球员角度来说多个球员属于一个球队 即为多对一数据表间一对多关系如下图:
- 当两个表建立一对多关系的时候,
"一"的那一端是父表,"多"的那一端是子表.
关联映射:多对多:
- 多对多关系也很常见,例如学生与选修课之间的关系,一个学生可以选择多门选修课,而每个选修课又可以被多名学生选择。
数据库中的多对多关联关系一般需采用中间表的方式处理,将多对多转化为两个一对多。- 数据表间多对多关系如下图:
- 中间表的主键生成方式:
①方式一:另外设置字段作为主键
②方式二:使用联合主键
关联映射:一对一:
一对一关系就如球队与球队所在地址之间的关系,一支球队仅有一个地址,而一个地址区也仅有一支球队。- 数据表间一对一主键关联:要求两个表的主键必须完全一致,通过两个表的主键建立关联关系。图示如下:
mysql反引号
反引号简介:
- 它是为了区分MYSQL的保留字与普通字符而引入的符号。
- 注意划重点:有MYSQL保留字作为字段的,必须加上反引号来区分!
- 所谓的保留字就是select database insert 这一类数据库的sql指令,当我们不得已要拿他们来做表名和字段名的时候我们必须要加反引号来避免编译器把这部分认为是保留字而产生错误。
- 当然在使用不是保留字的字符串时也可以加上反引号,这么加反引号只是作一个保险,这也是一个良好的sql建表习惯。
务必要记住:保留字既不能作为表名,也不能作为字段名,如果非要这么操作,请记住要增加反引号!
例如:
`tal_user`
`insert`
Mysql查询底层详解
连接查询简介:
a表 id name
1 张3
2 李四
3 王武
b表 id job parent_id
1 23 1
2 34 2
3 34 4
- a.id同parent_id 存在关系
- 内连接
select a.*,b.* from a inner join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
- 左连接
select a.*,b.* from a left join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
3 王武 null
- 右连接
select a.*,b.* from a right join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
null 3 34 4
- 完全连接
select a.*,b.* from a full join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
null 3 34 4
3 王武 null
- 结论:
①
左/右外连接包含left/right join左/右表所有行,如果左/右表中某行在右/左表没有匹配,则结果中对应行右/左表的部分全部为空(NULL)。②对于没有匹配的部分仅仅限于连接条件,如果还有其他where条件则范围缩小为where条件查询后的。
group by和聚合函数简介:
-
from test group by name:该句执行后,我们想象生成了虚拟表3,如下所图所示,生成过程是这样的:group by name,那么找name那一列,具有相同name值的行,合并成一行,如对于name值为aa的,那么<1 aa 2>与<2 aa 3>两行合并成1行,所有的id值和number值写到一个单元格里面。
-
group by 多个字段该怎么理解呢:如group by name,number,我们可以把name和number 看成一个整体字段,以他们整体来进行分组的。如下图
-
所以对于分组 : ①
若分组了,则不同值肯定分为不同组。②若没分组,有多个值时则需要使用聚合函数聚合起来,否则报错,没有多个值则无所谓。③如果没有聚合函数,你不能选择未分组的行--MySQL会给你随机值。 -
注意: ①SQL中只要用到聚合函数就不一定要用到group by。聚合函数是对一组值执行计算,并返回单个值,也被称为组函数。 聚合函数可以应用于SELECT 查询语句的 GROUP BY 子句的HAVING子句中,但不可用于WHERE语句中,因为WHERE是对逐条的行记录进行筛选。 ②聚合函数要使用的话,有一个前提,那就是是必须要有结果集。正如当初传智播客出的书中写到。 <1>根据mysql的执行步骤,当程序执行到where的时候,mysql是没有结果集的,所以聚合函数不能用在where后面。 <2>但聚合函数为什么就可以放在having后面呢?原因是使用having,前面一定要有分组,而分组的时候就已经有结果了,所以就有结果集,满足聚合函数前面一定要有结果集的要求。 ③
因此聚合函数可以在查询结果集中和having后使用,但不能在where后使用。④聚合函数在汇总前还可以在里面写表达式来计算字段之间的关系。⑤其实很多数据库不能在group by中用所在表中不存在的列的,这种要用要把前面的结果,做成派生表。 -
链接: ①SQL 的 group by和聚合函数(很好理解版) ②MySQL order by、group by底层实现及优化(非常详细) ③MySql分组查询(group by)并计算对应的字段之和及两个字段相乘之后的和
连接查询和子查询简介:
- 连接查询查询时默认
(即*)是查询两张表合并后的结果集:①
因此内连接相连时字段左右表都一样②因此左右连接相连字段左右表不一样③
因此对于连接字段底层是为两个,不是一个(也就是照着两个原表输出)。 - mysql中各种连接查询图解:
- 子查询:
子查询是把子查询出来的表供给父查询使用 - 链接: ①Mysql表连接查询 ②Mysql多表连接查询的执行细节
Mysql时间问题
时间使用datetime和char的区别:
- 存储类型,char的本质是定长字符串,datetime表现是时间类型,本质是int。精度要求相同时,char占用的空间更大。
- char可以存储时间的长度和精度可以完全由程序决定,datetime则由数据库本身决定。
- 作为索引的查询性能,datetime的存储类型更短,而且为int类型辨识度更高,在where或join时可以有更好的性能。
- 所以,datetime更节约空间,有更好的查询性能
(因为int类型比较好比较),如果datetime的长度或精度不满足需求,建议存储bigint类型的时间戳,没有必要将时间类型存为char。
Date,DateTime,TimeStamp和Time的解释和区别:
- timestamp与datetime的区别
①
DATETIME的默认值为null;TIMESTAMP的字段默认不为空(not null),默认值为当前时间(CURRENT_TIMESTAMP),如果不做特殊处理,并且update语句中没有指定该列的更新值,则默认更新为当前时间。这个区别就解释了为什么平时我们都不用可以管这个字段就能自动更新了,因为多数时候用的是timestamp;而此处用的是datetime,不会有自动更新当前时间的机制,所以需要在上层手动更新该字段。 ②DATETIME使用8字节的存储空间,TIMESTAMP的存储空间为4字节。因此,TIMESTAMP比DATETIME的空间利用率更高。这个区别解释了为啥timestamp类型用的多。 ③两者的存储方式不一样 ,对于TIMESTAMP,它把客户端插入的时间从当前时区转化为UTC(世界标准时间)进行存储。查询时,将其又转化为客户端当前时区进行返回。而对于DATETIME,不做任何改变,基本上是原样输入和输出。 ④两者所能存储的时间范围不一样 : <1>timestamp所能存储的时间范围为:’1970-01-01 00:00:01.000000’ 到 ‘2038-01-19 03:14:07.999999’; <2>datetime所能存储的时间范围为:’1000-01-01 00:00:00.000000’ 到 ‘9999-12-31 23:59:59.999999’。 - CURRENT_TIMESTAMP为什么能用于datetime类型? ①在mysql 5.6之前的版本,CURRENT_TIMESTAMP只能用于timestamp类型,5.6版本之后,CURRENT_TIMESTAMP也能用于datetime类型了,select version()查了一下数据库发现确实版本是5.6.29。
时间使用datetime和int的区别:
- 使用int存储时间时我们看不懂int字段的所代表的时间,我们就要用FROM_UNIXTIME转换:
select FROM_UNIXTIME('int') as time ,name from testint
总结:
- 链接: ①MySQL时间字段究竟使用INT还是DateTime型? ②MySQL中timestamp、datetime和int类型的区别与优劣
- 存储空间: ①在存储空间上,DATETIME 比其它人类型多了一倍的存储空间,但其实也就多了 4 个字节。现如今的系统越来越复杂,许多表的字段动辄十几行甚至几十行,多这 4 个字节其实没有什么影响,完全可以忽略不计,除非你的表字段全是 DATETIME 类型,才需要衡量一下。
- 显示格式:
①
INT 类型需要通过 MySQL 提供的函数或者程序本身额外进行处理才能显示为我们想要的日期格式,这个其实也不需要多少工作量。当然可能在直接使用工具连接数据库查看时,INT 数据阅读起来会很困难。实际在工作中,生产环境的数据库一般都是禁止使用图形化工具直接连接的。我们要么编写脚本,要么通过后台查看,这个完全可以让在程序处理时将数据转化为我们想要的格式,所以 INT 数据无法直接阅读这个缺点在现实中影响不大。 - 操作效率: ①INT在排序和查询时的效率是最高的,但也仅仅是高一丁点。当存在大量的查询和排序情况时,加个索引可能就完全解决了,再不然就分库分表,甚至是加缓存。而如果仅仅通过选择 INT ,只因为其本身的处理性能比别的高,就想获得巨大性能提升是不现实的。
- 跨时区: ①DATETIME 虽然和时区无关,但可以通过将其转化为时间戳,再将时间戳转化为其它任何时区时间。方法总比困难多。
- 特殊功能: ①TIMESTAMP 可以自动管理时间的更新,确实很方便。现在的许多框架,都有自动管理时间的功能,也不一定非得用 TIMESTAMP。
- 时间范围: ①这个我觉得是区别最大的,也是最需要我们去做抉择的地方。 ②在这四种数据类型中,对于时间范围,TIMESTAMP 和 INT(无符号)最多只支持到 2038 年。换句话说,到了 2038 年,我们可能会遇到像 千年虫 那样的问题。面对这个问题,有些人可能想都不用想就直接选择 DATETIME 或者 INT(有符号)了。如果你选择 INT(L:有符号),问题其实并没有解决。MySQL 所提供的将时间戳转化为日期格式的函数 FROM_UNIXTIME 仅支持32的数据,超出将溢出,返回的结果将是 NULL。 ③那我直接用一些编程语言自带的时间处理函数转不就不会有这个问题了? ④这个当然是没问题的,只是这样你可能就需要在结果输出前遍历转化一下结果,并且也不是所有的语言都能完全正确的处理这个问题,这个得要实际测试一下才知道,但我相信很多人是没有做这个测试的,而且你在设置表的时候,可能会忘记勾选“无符号”的选项。 ⑤这样看来,只有 DATETIME 最保险了。是的,这个也是我所推荐的。 ⑥综上,你可能觉得 DATETIME 是唯一的选择。其实不是,在现实生活中,我们面对的问题实在是太多了,这个问题的重要性在所有问题中的占比约等于零。不管是选择哪一种,短期来看,区别不大。至于2038年可能要面临的 千年虫 问题,也并非我们想的那么恐怖。你想想看,解决一个 千年虫 可能要花费几十亿,但如果现在解决,花费的可能远不止这几十亿(思考成本,测试成本等)。而等到2038年,可能很多公司都倒闭了,那岂不是白忙活了,所以完全不用去纠结这个问题。
为什么在开发框架中若字段的数据类型是int,一般都是对应于java类型中的Long类型?
- 下图展示MySQL中int分别为有符号及无符号的情况下的取值范围;展示java中Integer和Long的取值范围。从对比中可以看出,如果MySQL选择无符号的int类型时,它的取值范围是要超过java的Integer类型的,所有猜测为了确保能包括MySQL中int的所有取值范围,使用java中的Long映射最为可靠。
- 另外看下int类型是否有符号的设置:
unix时间戳简介:
- unix时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。Unix时间戳(英文为Unix epoch, Unix time, POSIX time 或 Unix timestamp)是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。
- UNIX时间戳的0按照ISO 8601规范为 :1970-01-01T00:00:00Z.一个小时表示为UNIX时间戳格式为:3600秒;一天表示为UNIX时间戳为86400秒,闰秒不计算。
- 在大多数的UNIX系统中UNIX时间戳存储为32位,这样会引发2038年问题或Y2038。
- 此外MySql数据库默认的时间戳是
10位数字:
使用 unix_timestamp 将时间转换成时间戳:
SELECT UNIX_TIMESTAMP('2018/12/20') FROM DUAL; -- 1545235200
SELECT UNIX_TIMESTAMP('2018-12-20 01:00:00') FROM DUAL; -- 1545238800
使用 from_unixtime 将时间戳转换成时间:
SELECT FROM_UNIXTIME(1545238800) FROM DUAL;
- 而java后台代码中使用Date.getTime()获取的时间戳为
13位数字(精确到毫秒)。①所以转换方法为(把毫秒数去掉,用于在sqlYog里查询展示,也可以在保存时直接去掉三位尾数,但这样会丢失时间戳精度) ②相反,mysql的默认时间戳转换java时间戳需要*1000。
SELECT f.*,FROM_UNIXTIME(FLOOR(f.`create_time`/1000)) FROM fin_sysnc_vendor f;
- 总结:
①MySQL 时间戳和Java返回的时间戳是不一样的
②例如: 当前时间是 2014-08-04 10:42:55.204000
<1>使用mysql时间戳函数UNIX_TIMESTAMP 返回的结果为: 1407120175.204000 <2>使用Java时间戳函数返回的结果为 : 1407120379000 ③很明显两者返回的值是不一样的: <1>mysql时间戳 计算方法是先计算2014-08-04 10:42:55 的时间戳,将该值除以10^3,然后加上后面毫秒作为返回结果 <2>但通常我们在程序中用Java返回的时间戳更加普遍, 那如何把mysql时间戳转换成JAVA时间戳: 将mysql时间戳结果做如下计算: 小数点左边数据*1000+小数点右边的值 = Java时间戳 1407120175*1000+204000 = 1407120379000 写成SQL语句如下:
select MID(UNIX_TIMESTAMP(createTime),1,10)*1000+MID(UNIX_TIMESTAMP(createTime),12,6)
AS t from student
Mysql时间注意:
- 两个日期可以比较大小,但不能直接相减,日期相减应该用函数DATEDIFF。
- 用字符串格式存的时间要转化成时间格式才能比较大小,使用TIME_FORMAT(t,f)函数。
- 插入或更新时,日期时间类型允许“不严格”语法,以DATETIME为例(其他日期时间类型雷同): ①YYYY-MM-DD HH:MM:SS 或 YY-MM-DD HH:MM:SS 格式的字符串。任何符号都可以用作日期部分或时间部分的间隔符。例如:“14-06-18 14:54:10”、“140618 14.54.10”、“14+06+18 14=54=10”是等价的。对于包含日期时间的字符串值,如果月、日、时、分、秒的值小于10,不需要指定两位数。例如:“2014-2-3 2:3:6”、“2014-02-03 02:03:06”是等价的。 ②YYYYMMDDHHMMSS 或 YYMMDDHHMMSS 格式的字符串。如果字符串对于日期时间类型是合法的就可以解释为日期时间类型。例如:“20140618145410” 和 “140618145410”将被解释为 “2014-06-18 14:54:10” ,但是 “20140618145480” 是不合法的(秒数不合法),将被解释为 “0000-00-00 00:00:00”。 ③YYYYMMDDHHMMSS 或 YYMMDDHHMMSS 格式的数字。如果该数字对日期时间类型是合法的就可以解释为日期时间类型。例如:“20140618145410” 和 “140618145410” 将被解释为 “2014-06-18 14:54:10” 。数值的长度应为6、8、12、14。如果数值长度是 8 或 14 位长,则假定为 YYYYMMDD 或 YYYYMMDDHHMMSS 格式。如果数值为 6 或 12 位长,则假定为 YYMMDD 或 YYMMDDHHMMSS 格式。
mysql日期/时间类型中datetime,date,time都可以以上述形式字符串插入,mysql会自动匹配转换插入日期/时间类型。若字符串不属于上述形式则无法正确匹配转换,则只能先将字符串转换成对应的日期/时间类型再插入。
职场经验:
- 对于一家公司,最重要的是如何在激烈的竞争中生存下去,MySQL 时间字段用 int 、 datetime 还是 timestamp?这个问题对于竞争毫无帮助。有些人可能会说:“细节决定成败”。我以前也很信奉这句话,并且事事以此为标准。结果就是做事比别人慢,把简单的问题想得过于复杂。
- 这个世界其实很复杂,许多道理并非一句话就能概括,凡事还是要根据不同场景对症下药。细节用在刀刃上,将事半功倍,要是用在可有可无的地方,那不过是瞎忙活,白费力气。
- 当然最后还是要做出选择的,咱也不能直接丢骰子随便乱选吧,那就是真的是不负责任了。下面我根据自己的一些经验给出一些自己的观点。 ①我们到一个公司上班,都不是一开始就从零构建一个项目,一般都是先从维护旧项目开始。在对旧项目添加或者修改功能时,我们可以直接按照该公司的习惯来选择存储时间字段的数据类型。前人用 DATETIME 我们就用 DATETIME,用 INT 我们就用 INT,除非公司有特别指定某种类型。而新项目我也是建议按照之前的设计,减少交接成本。 ②对于自己的个人作品,比如说开源项目,我是建议用 DATETIME,因为我们可以直接连接数据库,格式化的时间看起来多舒服呀。另外,咱们自己维护的项目,自然是想着永远存在的。你想想,十几年后,等孩子长大了,某一天来了兴致要给孩子炫耀一下老爹当年的项目,一打开网站竟然报错,原因是数据溢出,那多尴尬呀!然后你只能说稍等改一下。都一把年纪了,代码也不知道改不改得动。
MySQL中金额存储
简介:
-
MySQL 4.1以前的版本使用浮点运算实现DECIMAL的计算,这样会因为精度损失导致结果很奇怪。
-
MySQL 5.0之后DECIMAL类型支持精确计算了。
-
但是,归根结底,DECIMAL类型只是一个存储类型。因为CPU是不支持DECIMAL的直接计算,CPU本身是直接支持原生浮点计算,浮点计算的速度更快。
-
但在MySQL5.0以后的版本中MySQL服务器本身实现了DECIMAL的高精度计算。MySQL 5.0以后的版本中,是将数字打包保存到一个二进制字符串中(每4个字节保存9个数字) ①
例如:DECIMAL(18,9) = 9个小数点前数字+9个小数点后数字+小数点本身 = 4个字节+4个字节+1个字节 = 9个字节。MySQL 5.0以后的版本中DECIMAL类型允许最多65个数字。 ②例如:DECIMAL(M,N) 中M的取值范围是:1-65,而在早期版本中范围是:1-254,早期版本不会使用这么大的数字,且DECIMAL只是一种存储格式,计算的时候会被转换成DOUBLE类型计算。这样会丢失计算精度。 -
浮点类型在存储同样的范围时,通常比DECIMAL使用更少的空间。FLOAT使用4个字节,DOUBLE使用8个字节。
-
所以: ①只有在对小数进行精确计算的时候才是用DECIMAL类型。 ②
在数据量比较大的时候,可以考虑使用BIGINT类型代替DECIMAL类型,将需要存储的货币单位根据小数的位数乘以相应的倍数即可。假设要存储财务数据精确到万分之一,可以把所有金额乘以一百万,然后将结果存储在BIGINT里。这样可以同时避免浮点存储计算不精确和DECIMAL精确计算代价高的问题。
Navicat使用
Navicat查询表,字段,内容:
- 通常需要查询某个字段来自于哪张表,在navicat中没有直接查哪些表有指定字段名的功能,只能用sql来查。 ①(按字段名查表)查询哪些表有指定字段名(比如查字段名article_id)的SQL: SELECT * FROM information_schema.COLUMNS WHERE COLUMN_NAME='article_id'; 或者 SELECT table_name, column_name FROM information_schema.columns WHERE column_name = 'article_id'; 或者 SELECT column_name FROM information_schema.columns WHERE column_name LIKE '%搜索的字段%' AND table_schema = '你的数据库'; SELECT column_name FROM information_schema.columns WHERE column_name LIKE '%搜索的字段%' AND table_schema = '你的数据库' AND table_name = '你的表'; 这个SQL能查出所有你当前打开的链接下的所有数据库中的所有含有“article_id”字段名的表。
- (直接查表名)查表名: ①navicat右上角有个search框可迷糊查询你想要的表名
- 查内容。(按字段的内容查出表)在当前数据库的所有表中查含有指定字符串的字段(附带找出这些表) ①在数据库上右键——'在数据库中查找'——'查找'。输入你想要查找的字段内容。(只能查内容不能查字段,即按数据内容查出表而不是字段)
总结navicat:可查表名、内容,但不能查字段(需用SQL查)。但是这表名、字段、内容都能通过sql来查。
Navicat快捷键:
- Ctrl+Q 打开查询窗口
- Ctrl+/ 注释sql语句
- Ctrl+Shift +/ 解除注释
- Ctrl+R 运行查询窗口的sql语句
- Ctrl+Shift+R 只运行选中的sql语句
- F6 打开一个mysql命令行窗口
- Ctrl+L 删除一行
- Ctrl+N 打开一个新的查询窗口
- Ctrl+W 关闭一个查询窗口
- Ctrl+D 表的数据显示显示页面切换到表的结构设计页面,但是在查询页面写sql时是复制当前行
Navicat详解:
- “井号”# 是注释作用
- 在navicat的“”查询“功能中运行sql语句有缓存,测试sql语句执行时间,可能第一次3s,再多次点击运行就变成0.01s了。
- 链接: ①Navicat官方使用指南 ②Navicat Premium 常用功能讲解,分析sql,处理锁表,查看数据库运行