MyBatis ORM
ORM——Object Relation Mapping
其实就是一个Class对应一个Table,该Class里面的各个Property对应Table里面的各个字段。
MyBatis 缓存
其实就是将你的查询语句和查询结构会以键值对的形式存储到缓存中,就比如说:“Select * from atable” 这样你下次查询的时候,我们就会直接从缓存中获取这条查询结果的值了。当然任何增删改的操作都会导致缓存清空。
存储引擎
InnoDB以及MyISAM
- InnoDB支持事务(undo log),MyISAM不支持事务
- InnoDB支持外键,MyISAM不支持外键
- InnoDB是聚集索引,MyISAM是非聚集索引
- InnoDB支持行级锁和表级锁,MyISAM只支持表级锁
MySQL事务
隔离级别:
读未提交——脏读:读到未提交的东西
读已提交——不可重复读:两次读一个东西获得不同的数据(解决脏读:只读已Commit的数据)
可重复读——幻读:(解决不可重复读:MVCC机制)
MVCC机制其实就是快照读,就是每个数据都有一个隐藏字段Version,快照读读的就是某个Version的快照。Select(查)使用的就是快照读,而增删改(Insert,Delete,Update)使用的就是当前读。快照读是如何实现可重复读的呢?就是当你的事务开启时,第一次使用Select的时候,生成快照,之后哪怕你读取的这个东西被其他事务修改,并且Commit了,那你也是一样的,因为你读取的是快照里的数据。那么当前读又是怎么一回事呢?其实就是因为你修改数据的时候,需要知道最新的数据是什么呀。然后你修改的期间不应该让其他事务再进行操作,比如你读到一个数据age=11,然后在Buffer Pool里面修改期间,里面另一个事务直接把你这数据删除了,你回头一看,我勒个去,这数据都没了???这肯定是不行的,所以当前读是会加上一个排他锁的。在你执行完你的操作前其他事务是不允许操作的。
那么到这里,我们其实就可以获得一个简单的结论
快照读是通过MVCC来实现的,转门用于查 (可以解决幻读,不可重复读)
而当前读则是通过锁来实现的,专门用于增删改,行锁,间隙锁,表锁
那么接下来 我们来看看这几个锁吧
按共享还是排他来区分:
共享锁(for share mode):可以重复上锁,其他线程可读不可写
排他锁(for update):不可重复上锁,其他线程不可读不可写
按粒度来区分:
行锁:比如说 Select * From XXX Where id=1;
那么id为1的这一行就会被锁住,这可以解决重复读,但是解决不了幻读
表锁:顾名思义就是把整个表都锁起来,那其实就是效率很低的整体串行化了。
间隙锁:介于行锁和表锁中间,将特定的某行锁住,同时将这行的前面间隙和后面间隙也锁住。
索引
主键索引是唯一索引,也一般是聚簇索引,但是不是只有主键索引才是唯一索引,唯一索引效率是比较高的。
同时这里补充一下,覆盖索引,就是我们都知道一般来说非聚集索引里面存储的都是主键值,你要做一个回表的操作,才能拿到全部的数据。但是如果你的索引字段已经覆盖了你要查询的字段,那你就不需要做回表操作了,比如说 Select name From XXX where age='18' order by name(name是索引)
,那么你这里就可以不用回表了,你用来找值的索引已经可以满足你的需求了。
一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引。
日志
redo log的写入时机有三种
延迟刷,每秒写入一次OS buffer,然后直接调用syn方法写入磁盘中redo log file(宕机了的话你就少了一秒钟的数据)安全性最低,但是效率最高
实时写,实时刷,一旦commit直接写入OS buffer,然后直接调用syn方法写入磁盘中redo log file,安全性最高但是效率最低
实时写,延迟刷,一旦commit直接写入OS buffer,然后每秒调用syn方法写入磁盘中redo log file
一般比较推荐实时写,延迟刷,兼顾了效率和安全性。
主从分布式(Binlog)
一般为了数据安全,我们都会有mysql server master和slave同时来存储数据。
同时为了提高并发性能,我们也会实现读写分离。
所有增删改都发生在Master主库,查则发生在Slave库中
如何解决主从不一致问题:
- 在业务层,我们可以使用Loading界面,比如设置三秒跳转,这段时间里面我们就可以实现主从数据库数据同步。(但是这也是存在问题的,比如说你同步的数据量特别大,或者说你需要同步的时间很久,超过了你跳转的三秒)
- 部分操作直接从主库中读取,这样其实就不存在主从不一致的问题了,因为对于部分操作来说根本就不存在主从的关系。
- 半同步复制(其实严格意义上这不算解决了主从不一致),半同步就是确认至少一台Slave与Master有了相同的Version的binlog就返回成功的通知。当然这肯定解决了数据丢失的问题,毕竟你的数据一定已经存在某台从服务器上了。
- 异步复制,就是把binlog发过去给Slave就返回成功通知
分库分表
如果有自增能力的数据,比如说订单,随着时间的过去而增加,那么我们完全可以使用时间戳来做一个分库分表,比如每一年都有一个数据库,或者设置6个数据库,根据你的下单时间对6取模来决定你放入哪个数据库。
然后你的orderID就包含了时间戳的信息,你就可以根据你的OrderID获取到对应的储存在哪个数据库的index。
这个分区键挑选的原则就是让sql的操作尽量都发生在一个数据库中,减少跨/多库访问的频率,你可以是时间戳也可以是用户的ID。
可是问题来了如果你按照UserID划分的,那顾客是爽了,可是商户不爽呀,商户查询的时候肯定是要跨多个数据库的。那么我们其实可以使用冗余数据,根据UserID划分的数据存一份,根据商户ID划分的数据存一份,各自用各自,中间使用异步同步即可,因为商户做结算的时候对实时要求没那么高。
Redis数据结构
String:没什么好说的
Hash:就是类似Map的结构
List:按插入顺序有序,多个元素
Set:不可重复且无序,多个元素
ZSet:不可重复且按值排序,多个元素(底层实现是个跳表)
这里多说一下跳表这个结构,这个其实就是一个建立多级索引的链表,我们本来的链表是一个个遍历查找的嘛,但是如果你是有序的,那么显然我们有更高效的方法,我们可以引入二分算法的思想,就是你将原始的链表分成一段段,每一段的起始都跳跃连接到下一个节点。这样的话我们就很大程度减少了遍历的难度了。
Redis的数据持久化
RDB与AOF
RDB(Redis Database):其实就是快照式的数据存储,存储的是数据。
AOF(Append Only File):存储的是指令,当然这里就有涉及到指令优化,比如你连续设置一个键值对1000次,那我们就记录最后一条指令就好了。
Redis单线程模型
我们先搞明白一般处理请求的几种模式,一种是基于事件驱动,另一种就是基于线程,基于线程很好理解,就是你来一个请求,我就分配一个线程来处理你的请求。而比较高效的就是事件驱动,也就是所谓的多路复用,其实说白了,就是一个线程轮询,有IO请求了再去处理。就好像Linux下的Epoll,多个网络连接(多路),都只使用一个线程来处理(复用)
Redis使用多路复用来处理IO请求,所以单线程就可以处理多个连接的请求了。而且由于他是单线程的,非IO的任务处理也是该线程来处理,但是由于是内存中操作,所以这一步的性能也是很好的,而且减少了多线程的锁机制的消耗和线程切换的消耗,所以这是一个非常高效的中间件。
Redis缓存淘汰策略
其实就是看有没有设置过时间,有设置过期时间,就只在设置了过期时间的键值对里面采用随机,LRU以及ttl(按时间顺序)来淘汰,没设置的话,就是全局空间键值对采取随机,LRU以及ttl(按时间顺序)来淘汰。