缓存就是将我们经常查询的数据的结果保存到一个内存中(缓存就是内存中的一个对象),那么在下一次查询的时候就不用到数据库文件中查询,而是从内存中获取,从而减少与数据库的交付次数提高了响应速度。
假如有一条数据的查询量非常大,且内容基本不变,反复查询就会让数据库压力变大,这时我们就可以将数据存在内存缓存中,这样就大大提高的了查询效率,同时缓解了数据库压力。
为什么使用缓存?
首次访问时,查询数据库,并将数据存储到内存中;再次访问时直接访问缓存,减少IO、硬盘读写次数、提高效率
一级缓存范围:SqlSession
开启方式:默认开启
执行缓存条件:同一个sqlsession执行同一条sql
不执行缓存:不同sqlSession或两次查询之间执行了增删改操作
public interface UserMapper {
User getUserById(Integer id);
void deleteUserById(Integer id);
}
<mapper namespace="com.by.mapper.UserMapper">
<select id="getUserById" parameterType="int" resultType="com.by.pojo.User">
select * from user where id=#{id}
</select>
<delete id="deleteUserById" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>
从缓存中读取数据的情况:
@Test
public void testGoCache(){
SqlSession sqlSession = sessionFactory.openSession();
//拥有同一个sqlsession
UserMapper userMapper1 = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession.getMapper(UserMapper.class);
System.out.println("=============第一次查询============");
User user1 = userMapper1.getUserById(41); //执行查询
System.out.println(user1);
System.out.println("=============第二次查询============");
User user2 = userMapper2.getUserById(41);//执行查询?
System.out.println(user2);
}
在同一个SqlSession下同一条sql语句执行两次,可以看到第一次查询是从数据库中读取数据,第二次查询不查询数据库,从缓存中读取数据。
不从缓存中读取数据的情况:
情况一:拥有不同的SqlSession。
@Test
public void testNoGoCache(){
SqlSession sqlSession1 = sessionFactory.openSession();
SqlSession sqlSession2 = sessionFactory.openSession();
//拥有不同的sqlsession
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
System.out.println("=============第一次查询============");
User user1 = userMapper1.getUserById(41); //执行查询
System.out.println(user1);
System.out.println("=============第二次查询============");
User user2 = userMapper2.getUserById(41);//执行查询?不执行
System.out.println(user2);
}
在SqlSession不同的情况下,执行同一条sql语句我们可以看到两次查询均需要从数据中去读取数据。
情况二:拥有相同的sqlsession但两次查询之间执行增删改
@Test
public void testNoGoCache2(){
SqlSession sqlSession = sessionFactory.openSession();
//拥有同一个sqlsession
UserMapper userMapper1 = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession.getMapper(UserMapper.class);
System.out.println("=============第一次查询============");
User user1 = userMapper1.getUserById(41); //执行查询
System.out.println(user1);
System.out.println("=============两次查询之间执行增删改=============");
userMapper1.deleteUserById(1);
sqlSession.commit();
System.out.println("=============第二次查询============");
User user2 = userMapper2.getUserById(41);//执行查询
System.out.println(user2);
}
当我们在两条sql查询语句中插入一条删除语句进行commit提交时,我们可以看到,第二次查询依旧不能从缓存中给中读取数据,需要从数据库中读取。
一级缓存是SqlSession范围的缓存,当调用SqlSession的commit(),close()等方法时,就会清空一级缓存。
第一次发起查询用户id为 1 的用户信息,先去找缓存中是否有id为 1 的用户信息,如果没有,从数据库查询用户信息。 得到用户信息,将用户信息存储到一级缓存中。
如果sqlSession去执行 commit操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
二级缓存范围:SqlSessionFactory
开启方式:<cashe></cache>?
执行缓存条件:同一个sqlSessionFactrory,sqlsession执行commit或close
不执行缓存:不同sqlSessionFactrory 或 两次查询之间执行了增删改
二级缓存的开启有两种方式,可以全局开启,也可以局部开启
全局:在SqlMapConfig.xml 文件开启二级缓存
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
局部:配置相关的Mapper映射文件
<mapper namespace="com.by.dao.UserDao">
<!-- 开启二级缓存的支持 -->
<cache></cache>
<mapper namespace="com.by.mapper.UserMapper">
<!--局部开启二级缓存-->
<cache></cache>
<select id="getUserById" parameterType="int" resultType="com.by.pojo.User">
select * from user where id=#{id}
</select>
<delete id="deleteUserById" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>
从缓存中读取数据的情况:
@Test
public void testGoCache(){
SqlSession sqlSession1 = sessionFactory.openSession();
SqlSession sqlSession2 = sessionFactory.openSession();
//拥有相同的sqlSessionFactrory
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
System.out.println("=============第一次查询============");
User user1 = userMapper1.getUserById(41); //执行sql
System.out.println(user1);
sqlSession1.commit(); //第一次查询session执行commit或close,才会把数据写到二级缓存
System.out.println("=============第二次查询============");
User user2 = userMapper2.getUserById(41);//执行sql?不执行sql
System.out.println(user2);
}
拥有不同的sqlSession相同的sqlSessionFactrory,第一次查询session后进行commit(或者close),这是数据已经被写到二级缓存中,当二次查询时不需要从数据库中读取数据,直接从缓存中读取数据。
不从缓存中读取数据的情况:
情况一:拥有不同的sqlSessionFactrory
@Test
public void testNoGoCache() throws IOException {
//加载mybatis-config.xml
String resource = "mybatis-config.xml";
InputStream inputStream1 = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory1 = new SqlSessionFactoryBuilder().build(inputStream1);
InputStream inputStream2 = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory2 = new SqlSessionFactoryBuilder().build(inputStream2);
SqlSession sqlSession1 = sessionFactory1.openSession();
SqlSession sqlSession2 = sessionFactory2.openSession();
//拥有不相同的sqlSessionFactrory
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
System.out.println("=============第一次查询============");
User user1 = userMapper1.getUserById(41); //执行sql
System.out.println(user1);
sqlSession1.commit(); //第一次查询session执行commit或close,才会把数据写到二级缓存
System.out.println("=============第二次查询============");
User user2 = userMapper2.getUserById(41);//执行sql?执行sql
System.out.println(user2);
}
?当我们sqlSessionFactrory不同时,第二次执行相同的sql查询需要从数据库中读取数据。
情况一:拥有相同的sqlSessionFactrory但两次查询之间执行增删改
@Test
public void testNoGoCache2(){
SqlSession sqlSession1 = sessionFactory.openSession();
SqlSession sqlSession2 = sessionFactory.openSession();
//拥有相同的sqlSessionFactrory
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
System.out.println("=============第一次查询============");
User user1 = userMapper1.getUserById(41); //执行sql
System.out.println(user1);
sqlSession1.commit(); //第一次查询session执行commit或close,才会把数据写到二级缓存
System.out.println("=============两次查询之间执行增删改查=============");
userMapper1.deleteUserById(1);
sqlSession1.commit();
System.out.println("=============第二次查询============");
User user2 = userMapper2.getUserById(41);//执行sql?执行sql
System.out.println(user2);
}
?拥有相同的sqlSessionFactrory但两次查询之间执行增删改,第二次查询时不能从缓存中读取数据。
二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存结构图:
二级缓存是多个 SqlSession 共享的,其作用域是 mapper 的同一个 namespace,不同sqlSession 两次执行相同 namespace 下的 sql 语句且向 sql 中传递参数也相同,即最终执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据,而不再从数据库查询,从而提高查询效率。