短视频app代码,二级缓存使用需规范
短视频app代码,二级缓存机制使用场景分析
Mybatis的一级缓存仅在一个会话中被共享,会话之间的一级缓存互不影响,而Mybatis的二级缓存可以被多个会话共享,本小节将结合例子,对Mybatis中的二级缓存的使用机制进行分析。要使用二级缓存,需要对Mybatis配置文件进行更改以开启二级缓存,如下所示。
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
<setting name="cacheEnabled" value="true"/>
<setting name="localCacheScope" value="STATEMENT"/>
</settings>
上述配置文件中还将一级缓存的作用范围设置为了STATEMENT,目的是为了在例子中屏蔽一级缓存对查询结果的干扰。映射接口如下所示。
public interface BookMapper {
Book selectBookById(int id);
void updateBookPriceById(@Param("id") int id, @Param("bookPrice") float bookPrice);
}
要使用二级缓存,还需要在映射文件中加入二级缓存相关的设置,如下所示。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.learn.dao.BookMapper">
<!-- 二级缓存相关设置 -->
<cache eviction="LRU"
type="org.apache.ibatis.cache.impl.PerpetualCache"
flushInterval="600000"
size="1024"
readOnly="true"
blocking="false"/>
<resultMap id="bookResultMap" type="com.mybatis.learn.entity.Book">
<result column="b_name" property="bookName"/>
<result column="b_price" property="bookPrice"/>
</resultMap>
<select id="selectBookById" resultMap="bookResultMap">
SELECT
b.id, b.b_name, b.b_price
FROM
book b
WHERE
b.id=#{id}
</select>
<insert id="updateBookPriceById">
UPDATE
book
SET
b_price=#{bookPrice}
WHERE
id=#{id}
</insert>
</mapper>
二级缓存相关设置的每一项的含义,会在本小节末尾进行说明。
场景一: 创建两个会话,会话1以相同SQL语句连续执行两次查询,会话2以相同SQL语句执行一次查询。执行代码如下所示。
public class MybatisTest {
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream(resource));
SqlSession sqlSession1 = sqlSessionFactory.openSession(false);
SqlSession sqlSession2 = sqlSessionFactory.openSession(false);
BookMapper bookMapper1 = sqlSession1.getMapper(BookMapper.class);
BookMapper bookMapper2 = sqlSession2.getMapper(BookMapper.class);
System.out.println(bookMapper1.selectBookById(1));
System.out.println(bookMapper1.selectBookById(1));
System.out.println(bookMapper2.selectBookById(1));
}
}
执行结果如下所示。

Mybatis中的二级缓存开启时,每次查询会先去二级缓存中命中查询结果,未命中时才会使用一级缓存以及直接去查询数据库。上述结果截图表明,场景一中,SQL语句相同时,无论是同一会话的连续两次查询还是另一会话的一次查询,均是查询的数据库,仿佛二级缓存没有生效,实际上,将查询结果缓存到二级缓存中需要事务提交,场景一中并没有事务提交,所以二级缓存中是没有内容的,最终导致三次查询均是直接查询的数据库。此外,如果是增删改操作,只要没有事务提交,那么就不会影响二级缓存。
场景二: 创建两个会话,会话1执行一次查询并提交事务,然后会话1以相同SQL语句再执行一次查询,接着会话2以相同SQL语句执行一次查询。执行代码如下所示。
public class MybatisTest {
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream(resource));
SqlSession sqlSession1 = sqlSessionFactory.openSession(false);
SqlSession sqlSession2 = sqlSessionFactory.openSession(false);
BookMapper bookMapper1 = sqlSession1.getMapper(BookMapper.class);
BookMapper bookMapper2 = sqlSession2.getMapper(BookMapper.class);
System.out.println(bookMapper1.selectBookById(1));
sqlSession1.commit();
System.out.println(bookMapper1.selectBookById(1));
System.out.println(bookMapper2.selectBookById(1));
}
}
执行结果如下所示。

场景二中第一次查询后提交了事务,此时将查询结果缓存到了二级缓存,所以后续的查询全部在二级缓存中命中了查询结果。
场景三: 创建两个会话,会话1执行一次查询并提交事务,然后会话2执行一次更新并提交事务,接着会话1再执行一次相同的查询。执行代码如下所示。
public class MybatisTest {
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream(resource));
// 将事务隔离级别设置为读已提交
SqlSession sqlSession1 = sqlSessionFactory.openSession(
TransactionIsolationLevel.READ_COMMITTED);
SqlSession sqlSession2 = sqlSessionFactory.openSession(
TransactionIsolationLevel.READ_COMMITTED);
BookMapper bookMapper1 = sqlSession1.getMapper(BookMapper.class);
BookMapper bookMapper2 = sqlSession2.getMapper(BookMapper.class);
System.out.println(bookMapper1.selectBookById(1));
sqlSession1.commit();
System.out.println("Change database.");
bookMapper2.updateBookPriceById(1, 20.5f);
sqlSession2.commit();
System.out.println(bookMapper1.selectBookById(1));
}
}
执行结果如下所示。

场景三的执行结果表明,执行更新操作并且提交事务后,会清空二级缓存,执行新增和删除操作也是同理。
场景四: 创建两个会话,创建两张表,会话1首先执行一次多表查询并提交事务,然后会话2执行一次更新操作以更新表2的数据并提交事务,接着会话1再执行一次相同的多表查询。创表语句如下所示。
CREATE TABLE book(
id INT(11) PRIMARY KEY AUTO_INCREMENT,
b_name VARCHAR(255) NOT NULL,
b_price FLOAT NOT NULL,
bs_id INT(11) NOT NULL,
FOREIGN KEY book(bs_id) REFERENCES bookstore(id)
);
CREATE TABLE bookstore(
id INT(11) PRIMARY KEY AUTO_INCREMENT,
bs_name VARCHAR(255) NOT NULL
)
往book表和bookstore表中添加如下数据。
INSERT INTO book (b_name, b_price, bs_id) VALUES ("Math", 20.5, 1);
INSERT INTO book (b_name, b_price, bs_id) VALUES ("English", 21.5, 1);
INSERT INTO book (b_name, b_price, bs_id) VALUES ("Water Margin", 30.5, 2);
INSERT INTO bookstore (bs_name) VALUES ("XinHua");
INSERT INTO bookstore (bs_name) VALUES ("SanYou")
创建BookStore类,如下所示。
@Data
public class BookStore {
private String id;
private String bookStoreName;
}
创建BookDetail类,如下所示。
@Data
public class BookDetail {
private long id;
private String bookName;
private float bookPrice;
private BookStore bookStore;
}
BookMapper映射接口添加selectBookDetailById()方法,如下所示。
public interface BookMapper {
Book selectBookById(int id);
void updateBookPriceById(@Param("id") int id, @Param("bookPrice") float bookPrice);
BookDetail selectBookDetailById(int id);
}
BookMapper.xml映射文件如下所示。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.learn.dao.BookMapper">
<cache eviction="LRU"
type="org.apache.ibatis.cache.impl.PerpetualCache"
flushInterval="600000"
size="1024"
readOnly="true"
blocking="false"/>
<resultMap id="bookResultMap" type="com.mybatis.learn.entity.Book">
<result column="b_name" property="bookName"/>
<result column="b_price" property="bookPrice"/>
</resultMap>
<resultMap id="bookDetailResultMap" type="com.mybatis.learn.entity.BookDetail">
<id column="id" property="id"/>
<result column="b_name" property="bookName"/>
<result column="b_price" property="bookPrice"/>
<association property="bookStore">
<id column="id" property="id"/>
<result column="bs_name" property="bookStoreName"/>
</association>
</resultMap>
<select id="selectBookById" resultMap="bookResultMap">
SELECT
b.id, b.b_name, b.b_price
FROM
book b
WHERE
b.id=#{id}
</select>
<insert id="updateBookPriceById">
UPDATE
book
SET
b_price=#{bookPrice}
WHERE
id=#{id}
</insert>
<select id="selectBookDetailById" resultMap="bookDetailResultMap">
SELECT
b.id, b.b_name, b.b_price, bs.id, bs.bs_name
FROM
book b, bookstore bs
WHERE
b.id=#{id}
AND
b.bs_id = bs.id
</select>
</mapper>
还需要添加BookStoreMapper映射接口,如下所示。
public interface BookStoreMapper {
void updateBookPriceById(@Param("id") int id, @Param("bookStoreName") String bookStoreName);
}
还需要添加BookStoreMapper.xml映射文件,如下所示。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.learn.dao.BookStoreMapper">
<cache eviction="LRU"
type="org.apache.ibatis.cache.impl.PerpetualCache"
flushInterval="600000"
size="1024"
readOnly="true"
blocking="false"/>
<insert id="updateBookPriceById">
UPDATE
bookstore
SET
bs_name=#{bookStoreName}
WHERE
id=#{id}
</insert>
</mapper>
进行完上述更改之后,进行场景四的测试,执行代码如下所示。
public class MybatisTest {
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream(resource));
// 将事务隔离级别设置为读已提交
SqlSession sqlSession1 = sqlSessionFactory.openSession(
TransactionIsolationLevel.READ_COMMITTED);
SqlSession sqlSession2 = sqlSessionFactory.openSession(
TransactionIsolationLevel.READ_COMMITTED);
BookMapper bookMapper1 = sqlSession1.getMapper(BookMapper.class);
BookStoreMapper bookStoreMapper = sqlSession2.getMapper(BookStoreMapper.class);
System.out.println(bookMapper1.selectBookDetailById(1));
sqlSession1.commit();
System.out.println("Change database.");
bookStoreMapper.updateBookStoreById(1, "ShuXiang");
sqlSession2.commit();
System.out.println(bookMapper1.selectBookDetailById(1));
}
}
执行结果如下所示。

会话1第一次执行多表查询并提交事务时,将查询结果缓存到了二级缓存中,然后会话2对bookstore表执行了更新操作并提交了事务,但是最后会话1第二次执行相同的多表查询时,却从二级缓存中命中了查询结果,最终导致查询出来了脏数据。实际上,二级缓存的作用范围是同一命名空间下的多个会话共享,这里的命名空间就是映射文件的namespace,可以理解为每一个映射文件持有一份二级缓存,所有会话在这个映射文件中的所有操作,都会共享这个二级缓存。所以场景四的例子中,会话2对bookstore表执行更新操作并提交事务时,清空的是BookStoreMapper.xml持有的二级缓存,BookMapper.xml持有的二级缓存没有感知到bookstore表的数据发生了变化,最终导致会话1第二次执行相同的多表查询时从二级缓存中命中了脏数据。
场景五: 执行的操作和场景四一致,但是在BookStoreMapper.xml文件中进行如下更改。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.learn.dao.BookStoreMapper">
<cache-ref namespace="com.mybatis.learn.dao.BookMapper"/>
<insert id="updateBookStoreById">
UPDATE
bookstore
SET
bs_name=#{bookStoreName}
WHERE
id=#{id}
</insert>
</mapper>
执行代码如下所示。
public class MybatisTest {
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream(resource));
// 将事务隔离级别设置为读已提交
SqlSession sqlSession1 = sqlSessionFactory.openSession(
TransactionIsolationLevel.READ_COMMITTED);
SqlSession sqlSession2 = sqlSessionFactory.openSession(
TransactionIsolationLevel.READ_COMMITTED);
BookMapper bookMapper1 = sqlSession1.getMapper(BookMapper.class);
BookStoreMapper bookStoreMapper = sqlSession2.getMapper(BookStoreMapper.class);
System.out.println(bookMapper1.selectBookDetailById(1));
sqlSession1.commit();
System.out.println("Change database.");
bookStoreMapper.updateBookStoreById(1, "ShuXiang");
sqlSession2.commit();
System.out.println(bookMapper1.selectBookDetailById(1));
}
}
执行结果如下所示。

在BookStoreMapper.xml中使用<cache-ref>标签引用了命名空间为com.mybatis.learn.dao.BookMapper的映射文件使用的二级缓存,因此相当于BookMapper.xml映射文件与BookStoreMapper.xml映射文件持有同一份二级缓存,会话2在BookStoreMapper.xml映射文件中执行更新操作并提交事务后,会导致二级缓存被清空,从而会话1第二次执行相同的多表查询时会从数据库查询数据。
现在对Mybatis的二级缓存机制进行一个总结,如下所示。
Mybatis中的二级缓存默认开启,可以在Mybatis配置文件中的<settings>中添加<setting name="cacheEnabled" value="false"/>将二级缓存关闭;
Mybatis中的二级缓存作用范围是同一命名空间下的多个会话共享,这里的命名空间就是映射文件的namespace,即不同会话使用同一映射文件中的SQL语句对数据库执行操作并提交事务后,均会影响这个映射文件持有的二级缓存;
Mybatis中执行查询操作后,需要提交事务才能将查询结果缓存到二级缓存中;
Mybatis中执行增,删或改操作并提交事务后,会清空对应的二级缓存;
Mybatis中需要在映射文件中添加<cache>标签来为映射文件配置二级缓存,也可以在映射文件中添加<cache-ref>标签来引用其它映射文件的二级缓存以达到多个映射文件持有同一份二级缓存的效果。
最后,对<cache>标签和<cache-ref>标签进行说明。
<cache>标签如下所示。

<cache-ref>标签如下所示。

以上就是短视频app代码,二级缓存机制使用场景分析, 更多内容欢迎关注之后的文章
浙公网安备 33010602011771号