Статьи

Кэширование через MyBatis: настройка производительности OSCache поверх MyBatis

Эта статья представляет четвертое доказательство концепции из серии, описанной в предыдущей статье 4 Практические подходы для улучшения реализации уровня доступа к данным, и представляет, как реализовать OSCache поверх MyBatis, как добиться оптимальной конфигурации для него и личные мнения пользователей. Автор о выбранном подходе для уровня доступа к данным.

OSCache  — это инфраструктура Java, которая облегчает кэширование контента в веб-приложениях, которые можно бесплатно загрузить с https://java.net/projects/oscache; Этот проект был начат в 2011 году как подпроект openSymphony и поддерживался до исчезновения openSymphony . На протяжении всей статьи будут рассмотрены следующие аспекты:

1. Какую выгоду получит приложение от кэширования с использованием OSCache? Функции OSCache будут подробно описаны в этом разделе.

2 . Практическая реализация проекта OSCachePOC — в этом разделе ключевые концепции OSCache будут рассмотрены в практической реализации.

3. Резюме — Как производительность приложения была улучшена после этой реализации?

Код всех проектов, которые будут реализованы, можно найти по адресу  https://github.com/ammbra/CacherPoc    или, если вы заинтересованы только в текущей реализации, вы можете получить к нему доступ здесь:  https://github.com/ammbra. / CacherPoc / дерево / ведущий / OSCachePOC 

Какую выгоду получит приложение от кэширования с помощью OSCache?

В то время как некоторые проекты OpenSymphony продолжаются в других местах, потому что они были подхвачены внешними сторонами, OSCache не так повезло. Мне удалось собрать некоторую информацию о библиотеке из Google, и кажется, что:

  • OSCache — это решение для кэширования, которое включает в себя набор классов для выполнения детального динамического кэширования содержимого JSP, ответов сервлета или произвольных объектов.
  • OSCache обеспечивает в памяти и постоянные на диске кэши.
  • OSCache может позволить вашему сайту иметь толерантность к ошибкам (например, если ошибка происходит, например, например, ваша база данных становится недоступной, вы можете обслуживать кэшированный контент, чтобы люди могли по-прежнему просматривать сайт практически без ведома).

Учитывая небольшой объем доступной информации, давайте протестируем возможности OSCache в нашем проекте Proof of Concept.

Практическая реализация проекта OSCachePOC

Наша реализация OSCachePOC будет выглядеть так, как описано на диаграмме ниже:

Для тестирования производительности OSCache выполняются следующие настройки проекта:

1. Создайте новый проект Maven EJB из вашей IDE (этот тип проекта является платформой, предоставляемой NetBeans, но для тех, кто использует eclipse, вот полезное руководство  http://theopentutorials.com/examples/java-ee/ejb3/how. -to-create-a-ejb-3-x-project-using-maven-in-eclipse-part-2 /  ). В статье этот проект называется OSCachePOC.

2. Отредактируйте pom вашего проекта, добавив необходимые банки:

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-oscache</artifactId>
            <version>1.0.1</version>
        </dependency>
        <dependency>
            <groupId>opensymphony</groupId>
            <artifactId>oscache</artifactId>
            <version>2.4.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>jms</artifactId>
                    <groupId>javax.jms</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>     

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency> 

3. Добавьте драйвер подключения к базе данных, в данном случае apache derby:

        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derbyclient</artifactId>
            <version>10.11.1.1</version>
        </dependency>

4. Запустите команды mvn clean и mvn install для вашего проекта.

Если у вас есть ваш проект, давайте продолжим нашу реализацию MyBatis:

1. Сконфигурируйте в папке resources / com / tutorial / OSCachePOC / xml файл Configuration.xml с помощью:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="UNPOOLED">
                <property name="driver" value="org.apache.derby.jdbc.ClientDriver"/>
                <property name="url" value="jdbc:derby://localhost:1527/CRUD"/>
                <property name="username" value="cruddy"/>
                <property name="password" value="cruddy"/>
            </dataSource>
        </environment>         
    </environments>
    <mappers>               
        <mapper resource="com/tutorial/oscachepoc/xml/EmployeeMapper.xml" /> 
    </mappers>                   
</configuration>

2. Создайте в Java свою собственную реализацию SQLSessionFactory. Например, создайте com.tutorial.OSCachepoc.config. SQLSessionFactory

public class SQLSessionFactory {

 
    private static final SqlSessionFactory FACTORY;
 
    static {
        try {
            Reader reader = Resources.getResourceAsReader("com/tutorial/oscachepoc/xml/Configuration.xml");
            FACTORY = new SqlSessionFactoryBuilder().build(reader);
        } catch (Exception e){
            throw new RuntimeException("Fatal Error.  Cause: " + e, e);
        }
    }
 
    public static SqlSessionFactory getSqlSessionFactory() {
        return FACTORY;
    }
}

3. Создайте необходимые классы компонентов, которые будут отображаться в ваших результатах sql, например Employee:

public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    private Integer id;
    private String firstName;
    private String lastName;
    private String adress;
    private Date hiringDate;
    private String sex;
    private String phone;
    private int positionId;
    private int deptId;

    public Employee() {
    }

    public Employee(Integer id) {
        this.id = id;
    }


    @Override
    public String toString() {
        return "com.tutorial.oscachepoc.bean.Employee[ id=" + id + " ]";
    }
}

4. Создайте интерфейс IEmployeeDAO, который будет предоставлять реализацию ejb при внедрении:

public interface IEmployeeDAO {
        public List<Employee> getEmployees();   
}

5. Реализуйте вышеуказанный интерфейс:

@Stateless(name="oscacheDAO")
@TransactionManagement(TransactionManagementType.CONTAINER)
public class EmployeeDAO implements IEmployeeDAO {

    private static Logger logger = Logger.getLogger(EmployeeDAO.class);
    private SqlSessionFactory sqlSessionFactory;

   @PostConstruct
     public void init() {
        sqlSessionFactory = SQLSessionFactory.getSqlSessionFactory();
    }


    @Override
    public List<Employee> getEmployees() {
        logger.info("Getting employees with oscache.....");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<Employee> results = sqlSession.selectList("retrieveEmployees");
        sqlSession.close();
        return results;
    }
}

6. Создайте EmployeeMapper, который содержит запрос с именем «retrieveEmployees»

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.tutorial.oscachepoc.mapper.EmployeeMapper" >
     
    <resultMap id="results" type="com.tutorial.oscachepoc.bean.Employee" >
        <id column="id" property="id" javaType="integer" jdbcType="BIGINT" />
        <result column="first_name" property="firstName" javaType="string" jdbcType="VARCHAR"/>
        <result column="last_name" property="lastName" javaType="string" jdbcType="VARCHAR"/>
        <result column="adress" property="adress" javaType="string" jdbcType="VARCHAR" />
        <result column="hiring_date" property="hiringDate" javaType="date" jdbcType="DATE" />
        <result column="sex" property="sex" javaType="string" jdbcType="VARCHAR" />
        <result column="dept_id" property="deptId" javaType="integer" jdbcType="BIGINT" />
    </resultMap>     
<select id="retrieveEmployees" resultMap="results" >
        select id, first_name, last_name, hiring_date, sex, dept_id
        from employee   
    </select>  	
</mapper>																													

Если вы помните нашу настройку CacherPOC из предыдущей статьи, то вы можете протестировать свою реализацию, если добавите проект OSCachePOC в качестве зависимости и внедрите IEmployeeDAO внутри OSCacheServlet. Ваш файл CacherPOC pom.xml должен содержать:

      <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>OSCachePoc</artifactId>
            <version>${project.version}</version>
        </dependency>																															

и ваш сервлет должен выглядеть так:

@WebServlet("/OsCacheServlet")
public class OsCacheServlet extends HttpServlet {
   private static Logger logger = Logger.getLogger(OsCacheServlet.class);

    @EJB(beanName="oscacheDAO")
    IEmployeeDAO employeeDAO;
    private static final String LIST_USER = "/listEmployee.jsp";

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String forward= LIST_USER;
        List<Employee> results = new ArrayList<Employee>();
         for (int i = 0; i < 10; i++) {
            for (Employee emp : employeeDAO.getEmployees()) {
                logger.debug(emp);
                results.add(emp);
            }
            
            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                logger.error(e, e);
            }
        }
        req.setAttribute("employees", results);
        RequestDispatcher view = req.getRequestDispatcher(forward);
        view.forward(req, resp);
    }
    
}

Запустите реализацию CacherPoc, чтобы проверить, работает ли ваш уровень доступа к данным с MyBatis, или загрузите код, предоставленный по адресу https://github.com/ammbra/CacherPoc. 

Но если в базе данных хранится большое количество сотрудников или, возможно, получение числа 10xemployeesNo представляет большую нагрузку для базы данных. Также можно заметить, что запрос из EmployeeMapper.xml извлекает данные, которые почти никогда не изменяются (id, first_name, last_name, hiring_date, sex не могут измениться; единственное значение, которое может измениться во времени, это dept_id); поэтому можно использовать механизм кэширования.

Ниже описано, как этого можно добиться с помощью OSCache:

Кэширование Шаг 1.  Добавьте  файл oscache.properties в папку ресурсов и попробуйте аналогичную конфигурацию для него (если этот файл не определен, если не найден, клиент будет использовать настройки по умолчанию):

cache.capacity=1000
cache.memory=true
cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache

Приведенная выше конфигурация утверждает, что кэш настроен на хранение максимум 1000 значений в памяти с использованием алгоритма наименьшего количества недавно использовавшихся (LRU) для исключения.

Шаг 2. Кэширование. Обновите файл EmployeeMapper.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.tutorial.oscachepoc.mapper.EmployeeMapper" >
   
    <cache type="org.mybatis.caches.oscache.OSCache"/>
     
    <resultMap id="results" type="com.tutorial.oscachepoc.bean.Employee" >
        <id column="id" property="id" javaType="integer" jdbcType="BIGINT" />
        <result column="first_name" property="firstName" javaType="string" jdbcType="VARCHAR"/>
        <result column="last_name" property="lastName" javaType="string" jdbcType="VARCHAR"/>
        <result column="adress" property="adress" javaType="string" jdbcType="VARCHAR" />
        <result column="hiring_date" property="hiringDate" javaType="date" jdbcType="DATE" />
        <result column="sex" property="sex" javaType="string" jdbcType="VARCHAR" />
        <result column="dept_id" property="deptId" javaType="integer" jdbcType="BIGINT" />
    </resultMap> 
 
    <select id="retrieveEmployees" resultMap="results" useCache="true">
        select id, first_name, last_name, hiring_date, sex, dept_id
        from employee   
    </select>  
</mapper>

Добавив строку  <cache type = » org.mybatis.caches.oscache.OSCache» />  и указав в запросе useCache = «true», вы активируете кэш OSCache с возможностями ведения журнала для реализации DataAccessLayer.

Очистка, сборка и повторное развертывание проектов OSCachePOC и CacherPoc; Теперь дважды найдите ваших сотрудников, чтобы в кеше памяти можно было хранить значения. Когда вы запускаете запрос в первый раз, ваше приложение выполнит запрос к базе данных и получит результаты. Во второй раз, когда вы получите доступ к списку сотрудников, ваше приложение получит доступ к памяти в памяти.

Резюме — Как производительность приложения была улучшена после этой реализации?

Производительность приложения зависит от множества факторов

  • сколько раз кешированный фрагмент данных может и уменьшается приложением
  • доля времени отклика, которая уменьшается за счет кэширования

Закон Амдала может быть использован для оценки ускорения системы:

где P — пропорциональная скорость, а S — ускоренная.

Давайте возьмем нашу реализацию в качестве примера и вычислим скорость.

Когда приложение выполняет запрос без кэширования, выполняется транзакция JDBC, и в вашем журнале будет что-то похожее на:

INFO:   2014-12-22 18:01:30,020 [EmployeeDAO] INFO  com.tutorial.oscachepoc.dao.EmployeeDAO:38 - Getting employees.....
INFO:   2014-12-22 18:01:39,148 [JdbcTransaction] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction:98 - Setting autocommit to false on JDBC Connection [org.apache.derby.client.net.NetConnection40@1c374fd]
INFO:   2014-12-22 18:01:39,159 [retrieveEmployees] DEBUG com.tutorial.oscachepoc.mapper.EmployeeMapper.retrieveEmployees:139 - ==>  Preparing: select id, first_name, last_name, hiring_date, sex, dept_id from employee
INFO:   2014-12-22 18:01:39,220 [retrieveEmployees] DEBUG com.tutorial.oscachepoc.mapper.EmployeeMapper.retrieveEmployees:139 - ==> Parameters:
INFO:   2014-12-22 18:01:39,316 [retrieveEmployees] DEBUG com.tutorial.oscachepoc.mapper.EmployeeMapper.retrieveEmployees:139 - <==      Total: 13

при выполнении запросов с кэшированием OSCache транзакция JDBC выполняется только один раз (для инициализации кэша), после чего журнал будет выглядеть

INFO:   2014-12-17 07:59:50,902 [EmployeeDAO] INFO  com.tutorial.oscachepoc.dao.EmployeeDAO:38 - Getting employees with oscache.....
INFO:   2014-12-17 07:59:50,903 [AbstractConcurrentReadCache] DEBUG com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache:694 - get called (key=-2135929388:-430013521:com.tutorial.oscachepoc.mapper.EmployeeMapper.retrieveEmployees:0:2147483647:select id, first_name, last_name, hiring_date, sex, dept_id
        from employee)
INFO:   2014-12-17 07:59:50,903 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=1 ]
INFO:   2014-12-17 07:59:50,904 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=2 ]
INFO:   2014-12-17 07:59:50,904 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=3 ]
INFO:   2014-12-17 07:59:50,904 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=4 ]
INFO:   2014-12-17 07:59:50,904 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=5 ]
INFO:   2014-12-17 07:59:50,905 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=6 ]
INFO:   2014-12-17 07:59:50,905 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=7 ]
INFO:   2014-12-17 07:59:50,905 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=10 ]
INFO:   2014-12-17 07:59:50,905 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=11 ]
INFO:   2014-12-17 07:59:50,905 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=12 ]
INFO:   2014-12-17 07:59:50,905 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=14 ]
INFO:   2014-12-17 07:59:50,906 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=8 ]
INFO:   2014-12-17 07:59:50,906 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=9 ]
INFO:   2014-12-17 07:59:53,906 [EmployeeDAO] INFO  com.tutorial.oscachepoc.dao.EmployeeDAO:38 - Getting employees with oscache.....
INFO:   2014-12-17 07:59:53,906 [AbstractConcurrentReadCache] DEBUG com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache:694 - get called (key=-2135929388:-430013521:com.tutorial.oscachepoc.mapper.EmployeeMapper.retrieveEmployees:0:2147483647:select id, first_name, last_name, hiring_date, sex, dept_id
        from employee)
INFO:   2014-12-17 07:59:53,907 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=1 ]
INFO:   2014-12-17 07:59:53,907 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=2 ]
INFO:   2014-12-17 07:59:53,908 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=3 ]
INFO:   2014-12-17 07:59:53,909 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=4 ]
INFO:   2014-12-17 07:59:53,910 [OsCacheServlet] DEBUG com.tutorial.cacherpoc.OsCacheServlet:41 - com.tutorial.oscachepoc.bean.Employee[ id=5 ]

Давайте посмотрим, как оценивается каждый из наших 10- кратных запросов:  первый не кэшированный 10-кратный запрос занимал около 57 секунд и 51 миллисекунду, в то время как кэшированные запросы имели время 27 секунд и 15 миллисекунд.

Чтобы применить закон Амдхала для системы, необходим следующий вход:

  • Не кэшированное время страницы: 60 секунд
  • Время базы данных (с): 58 секунд
  • Время восстановления кэша: 27,081 секунд.
  • Доля: 96,6% (58/60) (P)

Ожидаемое ускорение системы, таким образом:

 1 / ((1 — 0,966) + 0,966 / (58 / 27,081)) = 1 / (0,034 + 0,966 / 2,104) = 2,028-кратное ускорение системы

Этот результат, конечно, можно улучшить, но целью этой статьи было доказать, что кэширование с использованием OSCache поверх MyBatis предлагает значительное улучшение того, что раньше было доступно до его реализации.

Узнайте больше от: