一 平常经常使用外键和外键和级联吗,可以说说你对它们的理解吗?
对于外键和级联,阿里巴巴开发手册这样说到:
【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
说明:以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风 险;外键影响数据库的插入速度
为什么不要用外键呢?大部分人可能会这样回答:
-
增加了复杂性:
a.每次做DELETE 或者UPDATE都必须考虑外键约束,会导致开发的时候很痛苦,测试数据极为不方便;b.外键的主从关系是定的,假如那天需求有变化,数据库中的这个字段根本不需要和其他表有关联的话就会增加很多麻烦。
-
增加了额外工作:
数据库需要增加维护外键的工作,比如当我们做一些涉及外键字段的增,删,更新操作之后,需要触发相关操作去检查,保证数据的的一致性和正确性,这样会不得不消耗资源;
(个人觉得这个不是不用外键的原因,因为即使你不使用外键,你在应用层面也还是要保证的。
所以,我觉得这个影响可以忽略不计。
)
-
外键还会因为需要请求对其他表内部加锁而容易出现死锁情况;
-
对分不分表不友好 :
因为分库分表下外键是无法生效的。
-
......
实际上,我们知道外键也是有很多好处的,比如:
-
保证了数据库数据的一致性和完整性;
-
级联操作方便,减轻了程序代码量;
-
......
我们常见的如java线程池、jdbc连接池、redis连接池等就是这类设计的代表实现。
这种设计会初始预设资源,解决的问题就是抵消每次获取资源的消耗,如创建线程的开销,获取远程连接的开销等。
就好比你去食堂打饭,打饭的大妈会先把饭盛好几份放那里,你来了就直接拿着饭盒加菜即可,不用再临时又盛饭又打菜,效率就高了。
除了初始化资源,池化设计还包括如下这些特征:
池子的初始值、池子的活跃值、池子的最大值等,这些特征可以直接映射到java线程池和数据库连接池的成员属性中。
数据库服务端还要维护一些缓存和用户权限信息之类的 所以占用了一些内存。
我们可以把数据库连接池是看做是维护的数据库连接的缓存,以便将来需要对数据库的请求时可以重用这些连接。
为每个用户打开和维护数据库连接,尤其是对动态数据库驱动的网站应用程序的请求,既昂贵又浪费资源。
在连接池中,创建连接后,将其放置在池中,并再次使用它,因此不必建立新的连接。如果使用了所有连接,则会建立一个新连接并将其添加到池中。
连接池还减少了用户必须等待建立与数据库的连接的时间。
-
UUID:
不适合作为主键,因为太长了,并且无序不可读,查询效率低。
比较适合用于生成唯一的名字的标示比如文件的名字。
-
数据库自增 id : 两台数据库分别设置不同步长,生成不重复ID的策略来实现高可用。
这种方式生成的 id 有序,但是需要独立部署数据库实例,成本高,还会有性能瓶颈。
-
利用 redis 生成 id : 性能比较好,灵活方便,不依赖于数据库。
但是,引入了新的组件造成系统更加复杂,可用性降低,编码更加复杂,增加了系统成本。
-
Twitter的snowflake算法 :
Github 地址:
https://github.com/twitter-archive/snowflake。
-
美团的Leaf分布式ID生成系统 :
Leaf 是美团开源的分布式ID生成器,能保证全局唯一性、趋势递增、单调递增、信息安全,里面也提到了几种分布式方案的对比,但也需要依赖关系数据库、Zookeeper等中间件。
感觉还不错。
美团技术团队的一篇文章:
https://tech.meituan.com/2017/04/21/mt-leaf.html 。
-
......
浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals 来判断。
具体原理和浮点数的编码方式有关,这里就不多提了,我们下面直接上实例:
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
System.out.println(a);// 0.100000024
System.out.println(b);// 0.099999964
System.out.println(a == b);// false
一种很常用的方法是:
使用使用 BigDecimal 来定义浮点数的值,再进行浮点数的运算操作。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);// 0.1
BigDecimal y = b.subtract(c);// 0.1
System.out.println(x.equals(y));// true
a.compareTo(b) : 返回 -1 表示小于,0 表示 等于, 1表示 大于。
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.compareTo(b));// 1
保留规则有挺多种,不需要记,IDEA会提示。
BigDecimal m = new BigDecimal("1.255433");
BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN);
System.out.println(n);// 1.255
我们在使用BigDecimal时,为了防止精度丢失,推荐使用它的 BigDecimal(String)构造方法来创建对象。
《阿里巴巴Java开发手册》对这部分内容也有提到如下图所示。
-
按照流的流向分,可以分为输入流和输出流;
-
按照操作单元划分,可以划分为字节流和字符流;
-
按照流的角色划分为节点流和处理流。
-
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
-
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
不管是文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么 I/O 流操作要分为字节流操作和字符流操作呢?
字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。
所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。
如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
荐
阅
读
如何优雅的导出 Excel
5. 团队开发中 Git 最佳实践
在看
本文分享自微信公众号 - Java后端(web_resource)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
相关文章
暂无评论...