数据库相关
1. 数据库连接池实现原理
需求: 数据库连接过程,每次访问数据库都需要打开一个链接,如果大型应用,每次访问数据库都重新建立连接,会严重影响性能。
解决: 建立数据库连接缓冲池。初始化一定数量的链接,需要的时候取出,使用完毕后不关闭再放回连接池以供下一次调用。
注意: a. 并发问题
多线程环境下,比方说获取链接,需要加同步。
b. 事务问题
Java中connection类本身提供了对事务的支持。可以通过设置autocommit(false)来关闭自动提交,然后自己设置何时提交和回滚。
简便思路是一个链接一个事务。
c. 资源配置
视具体系统而定。配置多少初始化连接,最大连接数,超时时间等根据具体情境来配置合适高效的时间。
c. 多数据库和多用户。
大型项目连接不同的数据库,可以通过单例模式来创建对应的连接池管理类。
一个数据库多用户,可以通过资源文件进行配置相同的url链接地址,有多个用户密码即可,
代码: 自己实现简单的数据库连接池。 TODO
2. 数据库事务四大特性
四大特性:
A 原子性
C 一致性
I 隔离性
不考虑隔离界别,出现如下集中情况:
脏读: 一个事务读到了另一个事务未提交的数据
不可重复读:同一条记录前后读取到的数据不一致
虚读(幻读): 同一条语句前后查询到的结果数量不一致
隔离级别:
级别\情况 脏读 不可重复读 虚读
Read uncommitted (读未提交): √ √ √
Read committed (读已提交): X √ √
Repeatable read (可重复读): X X √
Serializable (串行化): X X X
注: mysql默认是可重复读
Oracle只支持已提交读和串行化,默认是已提交读。
设置数据库隔离级别必须在事务开启之前,并只对当前事务管用。Connection.setAutoCommit(false).setTransactionIsolation(level)
D 持久性
3. 数据库引擎
A. 修改默认数据库引擎: set default_storage_engine = InnoDB.
修改某张表的数据库引擎: altertable engine = innoDB
B. Mysql数据库引擎: 默认情况下支持 ISAM, MyISAM(默认), Heap三个。 也可以使用INNODB和BERKLEY(BDB).
C. 区别和使用场景:
ISAM:
优点是查询速度快,占用内存少。
缺点是不支持事务,需要经常备份防止数据数据丢失。
MyISAM:
优点是扩展ISAM,支持索引和字段管理,表锁定优化并发读写操作。
缺点是不支持事务和外键。只支持表锁。需要经常使用OPTIMIZE TABLE来回复浪费空间。表损坏后无法恢复数据。
Heap(MEMARY):
优点是驻留内存,速度快。
缺点是数据容易丢失。
InnoDB: 优点是支持事务和外键, 支持表锁和行锁(默认)。
缺点是慢。
CSV:
逗号分隔值格式的文本行数据库。
缺点是不支持索引,所有列不能为空。
BlackHole(BDB):
优点:支持所有类型索引,也可以自定义索引。只有表结构,不存储数据,但是有日志记录。
4. 数据库锁
A. Mysql锁分类
表级锁:开销小,加锁快,不会出现死锁,锁粒度大,锁冲突概率高,并发度最低。
适用于查询为主,只有少量按索引条件更新数据的应用,如web应用.
页面锁:介于倆者之间,会出现死锁。
行级锁:开销大,加锁慢,会出现死锁, 所锁力度小,锁冲突概率低,并发度最高。
适用于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理系统。
悲观锁:可以通过排它锁select* from t for update 来触发。
适用于并发冲突比较激烈的情景。
优点是比较保守,缺点是增加开销。
乐观锁:将表添加一个version字段,每次写操作的时候,先查询出该字段的值,如果要进行更新操作,现判断此刻的version字段的值与查
询出来的version字段的值是否相同,如果相同,就进行更新,并将version字段的值加1,如果不同,说明该段时间有并发写操作, 则不进行该更新操作。
适用于竞争资源较小的环境。
共享锁:通过select* from t lock in share mode;来查询。
又称读锁[S锁],若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,
直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
排它锁:通过for update来锁定表。
又称写锁[X]。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上
的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。
5. 数据库索引
mysql支持的4中索引
B-Tree索引: 是平衡树而非二叉树。
Hash索引: 只有Heap(Memory)引擎支持。适用于key-value查询,不适合范围查询,只有在=的时候才会只用索引。
R-Tree索引: 空间索引,主要用于地理空间数据缩类型。
Full-text索引:
6. 数据库优化
A. 优化步骤:
a) 通过show status like ‘Com_%’等关键字(select,update,commit,rollback等)查看各种sql的执行频率,用来确认数据库的使用情况。
如果回滚次数较多,则需要考虑代码编写问题。
b) 可以通过慢查询日志来定位效率较低的sql语句。会将设置指定长时间的sql都存储到一个文件中来查看。
c) 通过explain + sql语句来分析低效的sql的执行计划。也可以通过show profiles来查看sql的具体执行过程。也可以通过trace文件来分
查看优化器如何选择执行计划。
d) 最后定位问题并优化。
B. 优化写法
a) Select 全部字段 而非使用 select * , 减少字段查询,减少IO操作。
b) 插入的时候使用多个值插入:Insert table(..) values(..),(..),(..)
c) 尽量使用关联查询而非子查询,后者需要建立临时表,影响速度。
d) 多表关联查询的时候扫描的行尽量少。可以通过在内部增加确定的条件减少结果集。
e) Between的效率比in高, In的效率比or的效率高。
f) 少用!= 用> 不等于是全表扫描。
g) 避免使用函数索引,mysql不支持函数索引,会扫描全表。
比如: select *from t where YEAR(d) >= 2016 最好写为: select* from t where d >= ‘2016-01-01‘;
h) Union all 的速度 比 union的速度快得多。
i) Like ‘keyword%’ 比 like ‘%keyword%’快的多,前置会使用索引,而后者不使用索引。
C. 优化手段
a) 建立合适的索引
b) 使用中间表减少统计的数据量。
c) 分库分区分表来提高查询速度。
7. 分布式事务
A. 介绍
在分布式系统下,如订单和库存,对应不同的数据库。一个操作对应俩个数据库,此时要保证一致性。
B. 解决方案
a) 基于消息中间件的俩阶段提交:消息事务+最终一致性。
在高并发场景下,将一个分布式事务拆分成一个消息事务(A系统的本地操作 + 发消息) 和一个B系统的本地操作。其中B系统的操
作由消息驱动。
发送端可靠性:
A系统事务成功,此时如果发送MQ消息成功:OK; 如果发送MQ消息失败:回滚;
A系统事务不成功:回滚
接收端可靠性:
不重复消费: 消费者在接收到消息并完成了本地事务操作之后,还要返回给消息系统一个通知以告知消息系统把这条消息删除掉,
如果此时网络出现中断,返回给消息系统删除该消息的通知失败,则消费者会再次消费该消息。
几率比较小,可以通过建立消息日志表来通过id判断是否是同一个消息来决定是否再次消费。
消费超时:自动发送指定次数。如果一直不成功,放入队列处理或者发邮件通知处理。
消费失败:安全度高的需要单独做一个操作日志,在操作中将详细信息记录下来,然后通过报警系统实时扫描分析该日志,
如果出现异常情况需要及时通过短信或邮件发送给相关人员先手动处理,然后在解决异常问题。
8. 分布式锁
A. 实现方式
a. 数据库方式
思路:创建一张表,里面方法名称字段为唯一的。想要执行某方法的时候向该表执行插入操作,执行完成之后删除该记录即可。因为
方法名称字段唯一,所以在并发的时候只能插入一条记录,其他的并不会执行。
也可以通过排他锁来实现,在最后通过提交事务即可释放锁。
缺点:
(1) 数据库单点故障,可以通过集群解决。
(2) 删除记录失败,会导致一直存在数据库中无法下次访问。当然,定时器定时扫描删除超时记录可以一定程度解决。
b. 缓存方式
思路: 采用redis缓存的方式。加锁的时候通过setnx命令加锁,锁的值为一个随机uuid串,并通过expire命令给锁设置过期时间,
防止死锁。释放锁的时候根据uuid串来判断是哪个锁,然后通过delete命令来释放锁。
优点: redis性能高,对命令的支持较好,实现方便。
c. Zookeeper方式