Spring boot整合Redis做数据缓存

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。

Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。

redis的官网地址,非常好记,是redis.io。(特意查了一下,域名后缀io属于国家域名,是british Indian Ocean territory,即英属印度洋领地)目前,Vmware在资助着redis项目的开发和维护。

下面是官方的bench-mark数据:
测试完成了50个并发执行100000个请求。
设置和获取的值是一个256字节字符串。
Linux box是运行Linux 2.6,这是X3320 Xeon 2.5 ghz。
文本执行使用loopback接口(127.0.0.1)。
结果:读的速度是110000次/s,写的速度是81000次/s

redis提供五种数据类型:string,hash,list,set及zset(sorted set)。
string(字符串)
string是最简单的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value,其上支持的操作与Memcached的操作类似。但它的功能更丰富。
redis采用结构sdshdr和sds封装了字符串,字符串相关的操作实现在源文件sds.h/sds.c中。

list(双向链表)
list是一个链表结构,主要功能是push、pop、获取一个范围的所有值等等。操作中key理解为链表的名字。

dict(hash表)
set是集合,和我们数学中的集合概念相似,对集合的操作有添加删除元素,有对多个集合求交并差等操作。操作中key理解为集合的名字。

dict中table为dictEntry指针的数组,数组中每个成员为hash值相同元素的单向链表。set是在dict的基础上实现的,指定了key的比较函数为dictEncObjKeyCompare,若key相等则不再插入。

zset(排序set)
zset是set的一个升级版本,他在set的基础上增加了一个顺序属性,这一属性在添加修改元素的时候可以指定,每次指定后,zset会自动重新按新的值调整顺序。可以理解了有两列的mysql表,一列存value,一列存顺序。操作中key理解为zset的名字。

zset利用dict维护key -> value的映射关系,用zsl(zskiplist)保存value的有序关系。zsl实际是叉数不稳定的多叉树,每条链上的元素从根节点到叶子节点保持升序排序。

Spring对mongoDB分布式数据库整合

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似的格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

它的特点是高性能、易部署、易使用,存储数据非常方便。主要功能特性有:

面向集合存储,易存储对象类型的数据。
模式自由。
支持动态查询。
支持完全索引,包含内部对象。
支持查询。
支持复制和故障恢复。
使用高效的二进制数据存储,包括大型对象(如视频等)。
自动处理碎片,以支持云计算层次的扩展性。
支持RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
文件存储格式为BSON(一种JSON的扩展)。
可通过网络访问。
所谓“面向集合”(Collection-Oriented),意思是数据被分组存储在数据集中,被称为一个集合(Collection)。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定义任何模式(schema)。Nytro MegaRAID技术中的闪存高速缓存算法,能够快速识别数据库内大数据集中的热数据,提供一致的性能改进。

模式自由(schema-free),意味着对于存储在mongodb数据库中的文件,我们不需要知道它的任何结构定义。如果需要的话,你完全可以把不同结构的文件存储在同一个数据库里。

存储在集合中的文档,被存储为键-值对的形式。键用于唯一标识一个文档,为字符串类型,而值则可以是各种复杂的文件类型。我们称这种存储形式为BSON(Binary Serialized Document Format)。

Spring boot JPA实现

每次开发基本上都是使用mybatis,对于hibernate,估计也忘了差不多。今天来重温了一下hibernate的配置实现。

第一步,肯定是配置文件:

/**
 * Created by alan on 2018/6/18.
 */
@Configuration
public class HibernateConfig {

    private String driver = "com.mysql.jdbc.Driver";
    private String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8";
    private String username = "root";
    private String password = "root";

    public HibernateConfig(){
    }

    @Bean
    public BasicDataSource dataSource(){
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setInitialSize(5);
        dataSource.setMaxIdle(100);
        dataSource.setMaxOpenPreparedStatements(100);

        dataSource.setCacheState(true);
        dataSource.setMaxTotal(1000);
        dataSource.setTestWhileIdle(true);
        dataSource.setValidationQuery("SELECT 1");
        dataSource.setTimeBetweenEvictionRunsMillis(3600000L);
        dataSource.setMinEvictableIdleTimeMillis(1800000L);
        dataSource.setTestOnBorrow(true);
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(){

        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");

        Properties properties = new Properties();
        properties.setProperty("hibernate.dialect","org.hibernate.dialect.MySQL5InnoDBDialect");
        properties.setProperty("hibernate.show_sql","true");
        properties.setProperty("hibernate.format_sql","true");
        properties.setProperty("hibernate.auto","update");

        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setPersistenceUnitName("SpringJPA");
        factoryBean.setJpaVendorAdapter(adapter);
        factoryBean.setDataSource(this.dataSource());
        factoryBean.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
        factoryBean.setValidationMode(ValidationMode.NONE);

        /**
         * scan @Entity
         */
        factoryBean.setPackagesToScan("com.example.demo.model","com.example.demo.repository");
        //factoryBean.setMappingResources("com/example/demo/repository/resource/User.hbm.xml");
        factoryBean.setJpaProperties(properties);
        return factoryBean;
    }

    @Bean
    public PlatformTransactionManager platformTransactionManager(){
        return new JpaTransactionManager(this.entityManagerFactory().getObject());
    }

    @Bean
    public PersistenceAnnotationBeanPostProcessor persistenceAnnotationBeanPostProcessor(){
        return new PersistenceAnnotationBeanPostProcessor();
    }
    
}

配置文件由dateSource、LocalContainerEntityManagerFactoryBean、PlatformTransactionManager组成,改有的都有了,现在来实现DAO层,emmmmm,本人比较懒,直接在service层里直接写代码了,如下:

public class TestByHibernateServiceImpl {

    private static final Logger logger = LoggerFactory.getLogger(TestByHibernateServiceImpl.class);

    @PersistenceContext
    private EntityManager manager;

    @Resource
    private LxUserMapper userMapper;

    public TestByHibernateServiceImpl() {
    }

    public List<LxUserModel> getAll() {
        Query query = manager.createNativeQuery("SELECT *  FROM lx_user ORDER BY id DESC",LxUserModel.class);
        return query.getResultList();
    }

    public LxUserModel getRow() {
//        return manager.createQuery("SELECT a  FROM LxUserModel a ORDER BY id DESC",LxUserModel.class).setMaxResults(1).getSingleResult();
        return userMapper.getRow();
    }

    public int add(LxUserModel userModel) {
        manager.persist(userModel);
        return userModel.getId();
    }

哇塞,跟昨天写的那篇文章比,正篇肯真简短,没错大部分代码都是EntityManager去实现了,很多人包括我都因为懒,不去写自己的DAO实现,而去用开源的,如果有时间还是建议自己实现或看一下EntityManager的原理。

/**
 * Created by alan on 2018/6/18.
 */

@Repository
public interface LxUserMapper extends JpaRepository<LxUserModel,Integer> {

    @Query(value = "SELECT * FROM lx_user ORDER BY id DESC limit 1",nativeQuery = true)
    LxUserModel getRow();
}

 

下面提供一个例子,在真实场景中,查询数据可是千奇百怪,所以按照自己的思维封装了几个通用的方法,当然啦,肯定是使用了EntityManager:

/**
 * Created by alan.luo on 2017/10/22.
 */

public abstract class AbstractDefaultRepository<E extends Serializable> extends AbstractDefaultBaseRepository<E> {

    @PersistenceContext
    private EntityManager manager;

    /**
     * get all the data.
     * @param page
     * @param where
     * @param order
     * @param isDesc
     * @return
     */
    @Override
    public List<E> getAll(int page, List<PredicatePojo> where, String order, boolean isDesc) {
        CriteriaBuilder builder = this.manager.getCriteriaBuilder();
        CriteriaQuery<E> query = builder.createQuery(this.entityClass);

        Root<E> root = query.from(this.entityClass);
        CriteriaQuery<E> select = query.select(root);
        if (order != null && !"".equals(order)){
            if (isDesc){
                select.orderBy(builder.desc(root.get(order)));
            }else {
                select.orderBy(builder.asc(root.get(order)));
            }
        }

        select.where(getListPredicate(where,builder,root));

        TypedQuery<E> typedQuery = this.manager.createQuery(select);
        typedQuery.setFirstResult((page - 1) * ConstantInit.PAGE_SIZE);
        typedQuery.setMaxResults(ConstantInit.PAGE_SIZE);
        return typedQuery.getResultList();
    }

    /**
     * search all the data.
     * @param page
     * @param condition
     * @param order
     * @param isDesc
     * @return
     */
    @Override
    public PagePojo<E> search(int page, List<PredicatePojo> condition, String order, boolean isDesc){

        CriteriaBuilder builder = this.manager.getCriteriaBuilder();
        CriteriaQuery<Long> query = builder.createQuery(Long.class);

        Root<E> root = query.from(this.entityClass);
        CriteriaQuery<Long> select = query.select(builder.count(root));

        select.where(getListPredicate(condition,builder,root));

        long total = this.manager.createQuery(select).getSingleResult();

        PagePojo<E> pages = new PagePojo<E>((long) page,total);
        pages.setList(this.getAll(page,condition,order,isDesc));
        return  pages;
    }

    @Override
    public E getRowById(int id) {
        return this.manager.find(this.entityClass,id);
    }

    @Override
    public void add(E o) {
        this.manager.persist(o);
    }

    @Override
    public void update(E o) {
        this.manager.merge(o);
    }

    @Override
    public void remove(E o) {
        this.manager.remove(o);
    }

    @Override
    public int removeById(int id) {
        CriteriaBuilder builder = this.manager.getCriteriaBuilder();
        CriteriaDelete<E> q = builder.createCriteriaDelete(this.entityClass);

        return this.manager
                .createQuery(q.where(builder.equal(q.from(this.entityClass).get("id"),id)))
                .executeUpdate();
    }

    /**
     *
     * List<PredicatePojo> where = new ArrayList<>();
     *  where.add(new PredicatePojo("id","81", Criteria.lt));
     *  where.add(new PredicatePojo("age","10", Criteria.gt));
     *
     * List<PredicatePojo> where = new ArrayList<>();
     * if (userName != null){
     *     userName = "%" + userName + "%";
     *     List<PredicatePojo> like = new ArrayList<>();
     *     like.add(new PredicatePojo("username",userName, Criteria.like));
     *     like.add(new PredicatePojo("mobile",userName, Criteria.like));
     *     like.add(new PredicatePojo("email",userName, Criteria.like));
     *
     *     PredicatePojo pojo = new PredicatePojo();
     *     pojo.setLikeObj(like);
     *     pojo.setCriteria(Criteria.like);
     *     where.add(pojo);
     * }
     *
     *
     *
     * parser predicate.
     * @param where
     * @param builder
     * @param root
     * @return
     */
    protected Predicate[] getListPredicate(List<PredicatePojo> where,CriteriaBuilder builder,Root<E> root){

        Predicate[] predicates = new Predicate[where.size()];
        Predicate[] like;

        if (where != null && where.size() > 0){
            Predicate p = null;

            for (int i = 0; i < where.size();i++){

                Criteria ca = where.get(i).getCriteria();
                switch (ca){
                    case equal:
                        p = builder.equal(root.get(where.get(i).getKey()),where.get(i).getValue());
                        break;
                    case notEqual:
                        p = builder.notEqual(root.get(where.get(i).getKey()),where.get(i).getValue());
                        break;
                    case isNull:
                        p = builder.isNull(root.get(where.get(i).getKey()));
                        break;
                    case isNotNull:
                        p = builder.isNotNull(root.get(where.get(i).getKey()));
                        break;
                    case gt:
                        p = builder.gt(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case lt:
                        p = builder.lt(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case ge:
                        p = builder.ge(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case le:
                        p = builder.le(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case isFalse:
                        p = builder.isFalse(root.get(where.get(i).getKey()));
                        break;
                    case isTrue:
                        p = builder.isTrue(root.get(where.get(i).getKey()));
                        break;
                    case not:
                        p = builder.not(root.get(where.get(i).getKey()));
                        break;
                    case like:
                        like = new Predicate[where.get(i).getLikeObj().size()];
                        for (int j = 0; j < where.get(i).getLikeObj().size();j++){
                            like[j] = builder.like(root.get(where.get(i).getLikeObj().get(j).getKey())
                                    ,where.get(i).getLikeObj().get(j).getValue());
                        }
                        p = builder.or(like);
                        break;
                    case notLike:
                        like = new Predicate[where.get(i).getLikeObj().size()];
                        for (int j = 0; j < where.get(i).getLikeObj().size();j++){
                            like[j] = builder.notLike(root.get(where.get(i).getLikeObj().get(j).getKey())
                                    ,where.get(i).getLikeObj().get(j).getValue());
                        }
                        p = builder.and(like);
                        break;
                }

                predicates[i] = p;
            }
        }
        return predicates;
    }

}

重温了一下JdbcTemplate读取数据库的例子

JdbcTemplate需要是一个DataSource实例去实现数据库链接,如下面代码:

private BasicDataSource dataSource;
private JdbcTemplate template;

public TestServiceImpl() {
    dataSource = new BasicDataSource();
    dataSource.setDriverClassName(driver);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);

    template = new JdbcTemplate(dataSource);
}

Spring提供了很多数据访问模板,这里使用的是JDBC,当然你也可以使用,比如orm.jpa.jpaTemplate、orm.jdo.JdoTemplate等。

访问模板实例化好了,接下来就是代码的实现,这里会给出3个例子,getAll、getRow、add。取全部,取一行,插入一行数据,直接看代码:

public List<LxUserModel> getAll() {
    RowMapper<List<LxUserModel>> rowMapper = new RowMapper<List<LxUserModel>>() {
        @Nullable
        @Override
        public List<LxUserModel> mapRow(ResultSet rs, int i) throws SQLException {

            List<LxUserModel> list = new ArrayList<>();

            ResultSetMetaData metaData = rs.getMetaData();
            int closNum = metaData.getColumnCount();

            do {
                LxUserModel m = new LxUserModel();
                m.setId(rs.getInt(1));
                m.setUsername(rs.getString(2));
                m.setLoginPassword(rs.getString(3));
                m.setNickname(rs.getString(4));
                m.setRealName(rs.getString(5));
                m.setEmail(rs.getString(6));
                m.setMobile(rs.getString(7));
                m.setSex(rs.getString(8));
                m.setAge(rs.getInt(9));
                m.setCreateTime(rs.getInt(10));
                list.add(m);

            } while (rs.next());

            return list;
        }
    };
    List<LxUserModel> list = template.queryForObject("SELECT * FROM `lx_user` WHERE 1 ORDER BY id DESC", rowMapper, (Object[]) null);

    return list;
}

调用模板方法queryForObject,返回的数据是一个List对象,这里需要我们实现RowMapper接口,并在mapRow方法中实现数据的组装。看完这里,如果你可以举一反三,那么不管是getRow还是getOne,我觉得对你而言都是很简单。下面来看看取一行的代码:

public LxUserModel getRow() {
    RowMapper<LxUserModel> rowMapper = new RowMapper<LxUserModel>() {
        @Nullable
        @Override
        public LxUserModel mapRow(ResultSet rs, int i) throws SQLException {

            LxUserModel m = new LxUserModel();
            m.setId(rs.getInt(1));
            m.setUsername(rs.getString(2));
            m.setLoginPassword(rs.getString(3));
            m.setNickname(rs.getString(4));
            m.setRealName(rs.getString(5));
            m.setEmail(rs.getString(6));
            m.setMobile(rs.getString(7));
            m.setSex(rs.getString(8));
            m.setAge(rs.getInt(9));
            m.setCreateTime(rs.getInt(10));
            return m;
        }
    };
    LxUserModel object = template.queryForObject("SELECT * FROM `lx_user` WHERE 1 ORDER BY id DESC limit 1", rowMapper, (Object[]) null);

    return object;
}

同样实现了RowMapper接口,只是返回的方法不一样,简单吧?

现在来看看add的方法,插入一行数据,意味着需要执行一条SQL语句。

public int add(LxUserModel userModel) {

    String sql = "INSERT INTO `lx_user`(`username`, `loginPassword`, `nickname`, `createTime`) VALUES ('%s','%s','%s','%s')";
    String finalSql = String.format(sql, userModel.getUsername(), userModel.getLoginPassword(), userModel.getNickname(), (System.currentTimeMillis() / 1000));

    logger.info("sql {}", finalSql);
    class StatementCallback2 implements StatementCallback<Integer> {

        @Nullable
        @Override
        public Integer doInStatement(Statement statement) throws SQLException, DataAccessException {
            return statement.execute(finalSql) ? 1 : 0;
        }
    }
    template.execute(finalSql);

    class ResRowMapper implements RowMapper<Integer> {

        @Nullable
        @Override
        public Integer mapRow(ResultSet resultSet, int i) throws SQLException {
            return resultSet.getInt("id");
        }
    }

    return template.queryForObject("select id  from `lx_user` where 1 order by id desc limit 1", Integer.class);
}

代码很直接粗暴,直接执行execute方法,这个方法是不会返回数据的,但是如果我们需要读取到刚刚插入数据的Id怎么办?这里返回使用
queryForObject方法只读取最后一行数据的id,并返回出去。

日志提取分析工具(java源码)

最近有个项目,是硬件结合的,硬件上传到服务器的日志,每天数百万条,有时候某个设备出问题了,因为日志的数据很混乱,很难查出具体的原因。

所以写了这个工具,主要是提高日志分析的效率,可以通过关键词提取日志数据。

工具使用了多线程、I/O等技术,本人技术有限,所以只能写到这样子,测试过很多次。

测试出来的数据:400MB的日志,5个线程:96~97秒完成分割,分割出来的日志大小大同小异,为什么不把分割出来的日志合并呢?因为线程的启动时间不是顺序的,加上本人懒,所以没做了。

不建议使用超过20个线程去处理日志。因为如果是2GB的数据,10个线程去处理,每个线程也只需要处理204.8MB。这个已经是非常快的效率了。