Bug简记-Spring返回字符串加引号
引言在开发过程中会遇到这种各样的bug,也是自己吃过的亏。以后在这里会把自己遇到的bug记录下来,吸取教训,避免犯同样的错误。
踩坑记录在与第三方对接的接口中,对方推送消息接口定义如果接收成功返回success,反之返回其他字符串,如果不是success字符串就会再重复推送三次。自己在代码中为:
123456@RequestMapping(value = "", method = RequestMethod.POST)@ResponseBodypublic String callBack() { //...something return "success";}
这段代码自我感觉没问题,但是线上发现即使返回的是success字符串也会重复推送。让对方排查了下,对方说我们推送的不是success字符串。WTF?看了日志*result[success]*没问题啊,直到自己亲自调试了下接口:
1234// 请求自己接口获取返回值String result = getResult();// 返回trueSystem.ou ...
基于Redis的分布式锁
引言目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式服务下各个服务同时访问共享资源时,分布式锁就派上用场了。redis用来做缓存很常见,它还有一个非常重要的功能就是做分布式锁。
采坑记录Redis分布式锁大部分人都会想到:setnx+lua,或者set key value px milliseconds nx,自己也是吃了这方面的亏。
事情的发展是,我们的服务是分布式服务,其中有个功能是调用第三方接口进行外呼,外呼接口中有个参数accessToken是需要另外两个参数通过HTTP请求换取。每个租户所有员工共用这一个accessToken,accessToken的有效期为120min。刚开始写的伪代码如下:
123456String redisKey = REDIS_KEY_PREFIX + "_" + accountId + "_" + appId + "_" + secret; String accessToken = jedis.get(redi ...
logback推荐配置
引言大约从16年,不管是我参与别人已搭建好的项目还是自己单独搭建的项目,日志框架基本都换成了logback。
# logback优点
内核重写、测试充分、初始化内存加载更小,这一切让logback性能和log4j相比有诸多倍的提升
logback非常自然地直接实现了slf4j,这个严格来说算不上优点,只是这样,再理解slf4j的前提下会很容易理解logback,也同时很容易用其他日志框架替换logback
logback有比较齐全的文档
logback当配置文件修改了,支持自动重新加载配置文件,扫描过程快且安全,它并不需要另外创建一个扫描线程
支持自动去除旧的日志文件,可以控制已经产生日志文件的最大数量
配置的正确姿势我们大部分Java后台都是Maven工程,标准目录如下:
包路径
说明
src/main/java
java源代码文件,编译到target/classes
src/main/resource
正式包资源库,编译时会复制到target/classes
src/test/java
测试java源代码文件,编译到target/test-classes ...
PG与MySQL选型分析
引言PostgreSQL(简称pg)是近几年增长率较快的开源数据库,很多企业由原来的MySQL转向pg,在这里对比这两大开源关系型数据库的优劣,以便使用时快速选型。
# PG与MySQL的一些特性对比
特性
PG
MySQL
口号性特点
最先进的开源数据库
最流行的开源数据库
SQL编程能力
强大的SQL能力,包括丰富的统计函数和统计分析,对BI有很好的支持
没有强大的统计功能支持
数据类型
丰富的数据类型支持,包括地理信息、几何图形、json、数组等,json也可以建立索引
在地理信息支持度上不如PG,不支持几何图形等数据类型
事务能力
完整的ACID事务支持
不是完整的支持ACID事务特性
join
支持nested-loop, sort-merge, hash三种类型
只支持nested-loop
Text类型
没有长度限制,可以直接访问,可以索引,可以全文索引
有长度限制
复杂查询
支持窗口函数,支持递归,支持with语句
不支持窗口函数、递归等
索引
多种索引类型,包括b-tree,hash,gin,gist等,可以对模 ...
分布式数据库和缓存双写一致性解析
引言缓存由于其高并发和高性能的特性,已经在项目中被广泛使用。在读取缓存方面,都是按照先从缓存中读取,缓存中不存在再从数据库加载同时存入缓存的流程操作。但是在更新缓存方面,对于更新完数据库,是更新缓存还是删除缓存。又或是先删除缓存再更新数据库,其实都有很大的争议。
策略说明:从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。因此, 接下来的讨论的思路不依赖于给缓存设置过期时间这个方案。
在这里讨论三种更新策略:
先更新数据库,再更新缓存
先删除缓存,再更新数据库
先更新数据库,再删除缓存
先更新数据库,再更新缓存这套方案普遍反对,有如下两种原因:
线程安全角度同时有请求A和请求B进行更新操作,那么会出现
(1)线程A更新了数据库(2)线程B更新了数据库(3)线程B更新了缓存(4)线程A更新了缓存
这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因 ...
数据库主从不一致
引言在开发过程中,进场遇到数据库主从分离来降低读写的压力,但是数据库主从同步是有延时的,这里聊一聊数据库主库与从库的一致性问题。
常见的数据库集群架构一主多从,主从同步,读写分离
如上图所示
一个主库提供写服务
多个从库提供读服务,可以增加从库提升读性能
主从之间同步
任何方案不要忘了本心,增加从库的本心是提升读性能
为什么会出现不一致主从同步有时延,这个时延期间读从库,可能读到不一致的数据。
如上图
服务发齐了一个写请求
服务又发起了一个读请求,此时同步未完成,读到一个不一致的脏数据
数据库主从同步最后才完成
任何数据冗余,必将引发一致性问题。
解决方法忽略任何脱离业务的架构设计都是耍流氓,绝大部分业务,例如:百度搜索,淘宝订单,QQ消息,58帖子都允许短时间不一致。
如果业务能接受,最推崇此法。
如果业务能够接受,别把系统架构搞得太复杂。
强制读主
如上图
使用一个高可用主库提供数据库服务
读和写都落到主库上
采用缓存来提升系统读性能
这是很常见的微服务架构,可以避免数据库主从一致性问题。
选择性读主强制读主过于粗暴,毕竟只有少量写请求,很短时 ...
MySQL清除表空间碎片
引言MySQL在数据表使用很长时间后,表上的B-Tree索引可能会碎片化,会降低查询的效率。碎片化的索引可能会以很差或者无序的方式存储在磁盘上,这时就需要对表进行碎片化整理。
碎片产生原因1、表的存储会出现碎片化,每当删除了一行内容,该段空间就会变为空白、被留空,而在一段时间内的大量删除操作,会使这种留空的空间变得比存储列表内容所使用的空间更大;
2、当执行插入操作时,MySQL会尝试使用空白空间,但如果某个空白空间一直没有被大小合适的数据占用,仍然无法将其彻底占用,就形成了碎片;
3、当MySQL对数据进行扫描时,它扫描的对象实际是列表的容量需求上限,也就是数据被写入的区域中处于峰值位置的部分;
例:一个表有1万行,每行10字节,会占用10万字节存储空间,执行删除操作,只留一行,实际内容只剩下10字节,但MySQL在读取时,仍看做是10万字节的表进行处理,所以,碎片越多,就会越来越影响查询性能。
查看表碎片大小1、查看某个表的碎片大小
1SHOW TABLE STATUS LIKE '表名';
结果中’Data_free’列的值就是碎片大小
2、列出 ...
计算密集型 VS IO密集型
引言 在开发过程中,经常会遇到多线程的问题,解决多线程的其中一种方式就是利用线程池,其中需要开启线程的数量便成为了我们关注的焦点。JAVA中有两种并发类型:计算密集型(CUP-bound)和IO密集型(I/O-bound)。
计算密集型(CPU-bound) 计算密集型,顾名思义就是应用需要非常多的CPU计算资源,CPU大部份时间用来做计算、逻辑判断等CPU动作的程序称之CPU bound。在多核CPU时代,我们要让每一个CPU核心都参与计算,将CPU的性能充分利用起来,这样才算是没有浪费服务器配置,如果在非常好的服务器配置上还运行着单线程程序,那将是非常大的浪费。对于计算密集型的应用,完全是靠CPU的核数来工作,所以为了让它的优势完全发挥出来,避免过多的上下文切换,比较理性的方案是:
1线程数 = CPU核数 + 1
为什么是 +1?因为即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保CPU的时钟周期不会被浪费。
也可以设置成CPU核数 x 2,这还是要看JDK的使用版本,以及CPU配置(服务器的CPU有超线程)。对于JDK1.8来说,里面增 ...