一、传统的缓存方式:
1、在函数执行前,先检查缓存中是否存在数据,如果存在则返回缓存数据
2、如果不存在,就需要在数据库的数据查询出来。
3、最后把数据存放在缓存中,当下次调用此函数时,就可以直接使用缓存数据,减轻了数据库压力。
二、redis的基本概念
1、什么是Redis
Redis本质上是一个Key-Value类型的内存数据库,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。
由于它的这一特性,Redis也成为一种流行的数据缓存工具。
2、redis的优点:
(1) 速度快,因为数据存在内存中,类似于HashMap, HashMap的优势就是查找和操作的时间复杂度都是O(1 )
(2) 支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写。
3、Redis的数据类型
redis是一种高级的key:value存储系统,其中value支持五种数据类型:
1.字符串(strings)
2.字符串列表(lists)
3.字符串集合(sets)
4.有序字符串集合(sorted sets)
5.哈希(hashes)
String—list—sets
Redis是单进程单线程的
三、Redis的相关问题
1、为什么redis需要把所有数据放到内存中?
为了达到最快的读写速度,并通过异步的方式写入磁盘。如果不把数据放入内存,磁盘的IO速度会严重影响redis的性能。
2、乐观锁与悲观锁
悲观锁:
总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。
乐观锁:
每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。
version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值
update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
3、redis的持久化
持久化:
Redis为了内部数据的安全考虑,会把本身的数据以文件形式保存到硬盘中一份,在服务器重启之后会自动把硬盘的数据恢复到redis的里边。
1:redis自身的持久化:
快照机制:在指定的时间间隔内将内存中的数据集快照写入磁盘。
save 900 1 #900秒内如果超过1个key被修改,则发起快照保存。
优势:一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这样非常方便进行备份。
劣势: 一旦发生故障停机, 可能会丢失好几分钟的数据。
AOF:redis会将每一个收到的写命令都通过write函数追加到文件中。
需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
实际开发的选择:应该同时使用两种持久化功能。
2、如何保证Redis的数据和MySQL中的数据一致?
读请求:
不要求强一致性的读请求,走redis,要求强一致性的直接从mysql读取
写请求:
数据首先都写到数据库,之后更新redis
可以在MySQL端定义CRUD触发器,在触发CRUD操作后写数据到Redis
4、MySQL中的数据超过Redis的容量
redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略)。
volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
5、 Redis解决高并发问题:————-————待解决
Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。
1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。
2.服务器角度,利用setnx实现锁。
注:对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用synchronized也可以使用lock;第二种需要用到Redis的setnx命令,但是需要注意一些问题。
、Redis分布式锁
在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应。
在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库。
Mybatis中的#与$的区别
1 #{变量名}可以进行预编译、类型匹配等操作
select * from tablename where id = #{id}
假设id的值为12,其中如果数据库字段id为字符型,那么#{id}表示的就是'12',如果id为整型,那么id就是12.
${变量名}不进行数据类型匹配,直接替换。
使用#在很大程度上可以防止sql注入。
其它的一些JAVA基础问题:
1:IO与NIO
IO—面向流—阻塞IO—无
NIO—面向缓冲—非阻塞IO—选择器
2:hibernate访问数据库慢
1、Hibernate的并发机制
Hibernate的Session对象是非线程安全的,对于单个请求,单个会话,单个的工作单元,它通常只使用一次, 然后就丢弃。
解决方案:设置事务隔离级别。
串行化:隔离级别最高 :别的事务在旁边看的机会都没有
可重复读:读的时候加锁,别的线程不能修改,所以可以重复读。但是这种方法锁不住insert的数据
已提交数据读: 等别的线程修改完了,才能读。
Read Uncommitted:未提交数据读。隔离级别最差
乐观锁和悲观锁
2、在数据库中查询速度很慢怎么解决:
1.建索引
2.减少表之间的关联
3.优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据量大的表排在前面
4.简化查询字段,没用的字段不要
3、Hibernate的优化:
懒加载:
Customer c1 = session.load(Customer.class, 1);
// Customer c1 = session.get(Customer.class, 1);
load方法采用的策略是延迟加载;get方法采用的策略是立即加载
LOAD:
User user=session.load(User.class, "1");
当调用load()方法的时候会返回一个目标对象的代理对象,在这个代理对象中只存储了目标对象的ID值,只有当调用除ID值以外的属性值的时候才会发出SQL查询的。
如果查询的数据非常大,例如说它里面有一些大的字段,这个时候建议采用load方法,不要一上来就立即加载,把内存占满,这样可以让我们的性能得到一部分的提升。
3、线程池 java.util.concurrent.Executors
1、 Thread 类中的start() 和 run() 方法有什么区别?
start()内部调用了run()方法。
当调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。
2、如果你提交任务时,线程池队列已满。会时发会生什么?
事实上如果一个任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个RejectedExecutionException异常。
牛逼
3、atomic
atomic可以保证+-的原子性操作
4、singleton
单例对象的类必须保证只有一个实例存在
线程安全的懒汉模式
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){
}
public static synchronized SingletonDemo getInstance(){
if(instance==null){
instance=new SingletonDemo();
}
return instance;
}
}
5、线程池
一个线程池包括以下四个基本组成部分:
1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
ExecutorService pool=Executors.newCachedThreadPool();
//创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
Thread t1=new MyThread();
Thread t2=new MyThread();
//将线程放到池中执行;
pool.execute(t1);
pool.execute(t2);
//关闭线程池
pool.shutdown();
Mybatis配置多数据源:
6、Mybatis的缓存机制
mybatis的一级缓存:
SqlSession级别的缓存,在操作数据库的时候需要先创建SqlSession会话对象,在对象中有一个HashMap用于存储缓存数据,此HashMap是当前会话对象私有的,别的SqlSession会话对象无法访问。
具体流程:
1.第一次执行select完毕会将查到的数据写入SqlSession内的HashMap中缓存起来
2.第二次执行select会从缓存中查数据,如果select相同并且传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率
mybatis的二级缓存:
mapper级别的缓存
也就是同一个namespace的mapper.xml,当多个SqlSession使用同一个Mapper操作数据库的时候,得到的数据会缓存在同一个二级缓存区域
不同的 SqlSession 两次执行相同的 namespace 下的 sql 语句,会执行相同的 sql,第二次查询只会查询第一次查询时读取数据库后写到缓存的数据,不会再去数据库查询。
二级缓存默认是没有开启的。需要在setting全局参数中配置开启二级缓存
网友评论