分布式ID生成
- UUID
UUID是16字节128位长的数字,通常以36字节的字符串表示,示例如下:
3F2504E0-4F89-11D3-9A0C-0305E82C3301
UUID的生成基于多项客观因素,如时间等,所以可以保证全局唯一性。
缺点:无法保证ID自增。而且因为随机生成,mysql的B+树索引,在入库的时候会有很多节点分裂的相关操作,降低性能。 - 数据库自增
基于数据库的Auto_increament机制,可以获得全局唯一id,并且id自增。
缺点:这种分布式id严重依赖于数据库,数据库出现问题的时候会导致雪崩。而且但数据库的情况高并发时,性能不高,多数据库分库时又很难进行扩展,例如A库,1、3、5、7,B库,2、4、6、8。 - SnowFlake算法
生成的id共64位
第一位为0,无实际意义
第二位开始,41位为时间戳,精确到毫秒级
第42位开始,10位机器位,包括5位数据中心位和5位工作机器节点位
最后12位,同一毫秒内id自增
即不依赖数据库,又保证id自增,性能也很nice
缓存淘汰策略
- FIFO
先进先出
双向链表实现
可用map保存key的位置,来提高性能 - LRU
最近最久被使用
双向链表实现,被使用的key转到链表头部,每次淘汰时删除最后链尾元素 - LFU
最近最少被使用
队列实现,每次插入数据放入队列尾,每次使用数据对队列重新排序,队满时删除队头元素
分布式锁
分布式锁实现方式一般有三种:基于数据库,基于zookeeper临时有序节点,基于redis的nx、ex参数
基于redis的分布式锁设计:
加锁:加上NX、EX参数(在把NX、EX作为参数写在同一条语句中,能保证操作的原子性),NX在没有key的时候才创建key并加入缓存,EX设置缓存超时时间,设置超时时间可以防止死锁等情况。
阻塞锁:加锁时多线程竞争,阻塞,循环等待,在循环时可使线程sleep一定时间,防止一直拿不到锁而一直循环请求消耗CPU资源。也可以自定义阻塞时间,每次循环sleep时阻塞时间减去sleep时间,当为0时,等待时间过长未获取锁,返回错误。
解锁:在redis中把key删除就表示解锁。但是会有情况,有线程A,set了一个过期时间10s的锁,但是执行了20秒,在后10s里,另一个线程B,set了这个锁并执行完释放,导致B释放了A的锁。对这种问题,在加锁的时候每个线程设置一个自身独有的key,删除缓存释放锁的时候判断是不是自身的锁就ok。
注:redis中的原子操作也可以用lua脚本实现,暂时还不会,>.>
缓存
本地缓存:Guava Cache,Encache
分布式缓存:Redis,Memcached
Guava
google开发的java核心增强库,其中包含:
基本工具
快速失败拒绝null
前置条件
object常用方法
强大的排序工具
异常和错误
集合
不可变集合
新集合
集合扩展工具类
缓存
本地缓存,支持多种淘汰策略
并发
回调
服务维持
字符串处理
多种工具,分割、连接等等
原生类型
对java原生类型的扩展
区间
可比较类型的区间(不太懂)
I/O
简化I/O操作,尤其是文件流操作
散列
比hashcode更复杂的散列实现
布隆过滤器
事件总线
发布订阅模式组件通信
数学工具类
反射
秒杀系统
乐观锁更新 + 分布式限流 + Redis 缓存 + Kafka 异步
sql优化
- 字段的默认值不设置为null
设置为null的值可能会带来和预期不一致的查询结果 - 在数据区分不明显的情况下不使用索引
数据区分不明显的情况下,使用索引并不会加快查询速度,反而会额外花费时间来维护索引树从而降低性能 - 严格按照数据格式写sql
如果写的sql中数据类型和数据库中的数据类型不一致,sql会做强制数据转换,耗费大量时间 - 需要join的两表字段类型相同
- 明确知道有几条数据时
指明查询数据条数,停止游标移动加快查询速度
select name from user where username='zhangsan' limit 1
- 避免索引失效
- 在字段上进行计算不会使用索引
select name from user where FROM_UNIXTIME(create_time) < CURDATE();
应该修改为:
select name from user where create_time < FROM_UNIXTIME(CURDATE());
- 负向查询不会使用索引
例如:
select name from user where id not in (1,3);
可以的情况下改写成
select name from user where id in (2,4);
- 前缀匹配不会使用索引
例如:
select name from user where name like '%zhangsan'
频繁使用的前缀匹配可以替换为全文检索
4. 注意最左前缀匹配情况
如果给 user 表中的 username pwd 字段创建了复合索引那么使用以下SQL 都可以命中索引:
select username from user where username='zhangsan' and pwd ='123'
select username from user where pwd ='123' and username='zhangsan'
select username from user where username='zhangsan'
但是使用
select username from user where pwd ='123'
不会使用索引
算法
- 链表排序
归并法 - 数组右移k位
在不借用额外存储空间的情况下,保证时间复杂度最低。
将数组翻转,再对前k位翻转,后n-k位翻转即可,时间复杂度为n
进程、线程通信方式
进程间通信方式:管道,消息队列,信号量(semaphore),共享内存
线程通信方式:全局变量,wait、notify、notifyall
JDK监控和故障处理
命令
- jps -l
jps命令可以查看java进程启动类的相关信息 - jstat -[options] [vmid]
通过输入参数可以查看各种状态信息,vmid是进程id,可以用jps命令查看
jstat -class vmid :显示 ClassLoader 的相关信息;
jstat -compiler vmid :显示 JIT 编译的相关信息;
jstat -gc vmid :显示与 GC 相关的堆信息;
jstat -gccapacity vmid :显示各个代的容量及使用情况;
jstat -gcnew vmid :显示新生代信息;
jstat -gcnewcapcacity vmid :显示新生代大小与使用情况;
jstat -gcold vmid :显示老年代和永久代的信息;
jstat -gcoldcapacity vmid :显示老年代的大小;
jstat -gcpermcapacity vmid :显示永久代大小;
jstat -gcutil vmid :显示垃圾收集信息; - jinfo [vmid]
可以查看虚拟机各项参数 - jmap
生成堆转储快照 - jhat
用于分析 heapdump 文件,它会建立一个 HTTP/HTML 服务器,让用户可以在浏览器上查看分析结果 - jstack
命令用于生成虚拟机当前时刻的线程快照。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合
监控工具
- JConsole
JConsole 是基于 JMX 的可视化监视、管理工具。可以很方便的监视本地及远程服务器的 java 进程的内存使用情况。可以在控制台输出console命令启动或者在 JDK 目录下的 bin 目录找到jconsole.exe然后双击启动。 - Visual VM
可以监控Java 虚拟机 上运行的 Java 应用程序的详细信息。在 VisualVM 的图形用户界面中,您可以方便、快捷地查看多个 Java 应用程序的相关信息。