7、redis数据库&jedis&省份缓存案例

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

Redis

1. 概念 redis是一款高性能的NOSQL系列的非关系型数据库

1.1.什么是NOSQL
        NoSQL(NoSQL = Not Only SQL)意即“不仅仅是SQL”是一项全新的数据库理念泛指非关系型的数据库。
        随着互联网web2.0网站的兴起传统的关系数据库在应付web2.0网站特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心暴露了很多难以克服的问题而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战尤其是大数据应用难题。

        1.1.1.    NOSQL和关系型数据库比较
            优点
                1成本nosql数据库简单易部署基本都是开源软件不需要像使用oracle那样花费大量成本购买使用相比关系型数据库价格便宜。
                2查询速度nosql数据库将数据存储于缓存之中关系型数据库将数据存储在硬盘中自然查询速度远不及nosql数据库。
                3存储数据的格式nosql的存储格式是key,value形式、文档形式、图片形式等等所以可以存储基础类型以及对象或者是集合等各种格式而数据库则只支持基础类型。
                4扩展性关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难。

            缺点
                1维护的工具和资料有限因为nosql是属于新的技术不能和关系型数据库10几年的技术同日而语。
                2不提供对sql的支持如果不支持sql这样的工业标准将产生一定用户的学习和使用成本。
                3不提供关系型数据库对事务的处理。

        1.1.2.    非关系型数据库的优势
            1性能NOSQL是基于键值对的可以想象成表中的主键和值的对应关系而且不需要经过SQL层的解析所以性能非常高。
            2可扩展性同样也是因为基于键值对数据之间没有耦合性所以非常容易水平扩展。

        1.1.3.    关系型数据库的优势
            1复杂查询可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。
            2事务支持使得对于安全性能很高的数据访问要求得以实现。对于这两类数据库对方的优势就是自己的弱势反之亦然。

        1.1.4.    总结
            关系型数据库与NoSQL数据库并非对立而是互补的关系即通常情况下使用关系型数据库在适合使用NoSQL的时候使用NoSQL数据库
            让NoSQL数据库对关系型数据库的不足进行弥补。
            一般会将数据存储在关系型数据库中在nosql数据库中备份存储关系型数据库的数据

    1.2.主流的NOSQL产品
        •    键值(Key-Value)存储数据库
                相关产品 Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
                典型应用 内容缓存主要用于处理大量数据的高访问负载。 
                数据模型 一系列键值对
                优势 快速查询
                劣势 存储的数据缺少结构化
        •    列存储数据库
                相关产品Cassandra, HBase, Riak
                典型应用分布式的文件系统
                数据模型以列簇式存储将同一列数据存在一起
                优势查找速度快可扩展性强更容易进行分布式扩展
                劣势功能相对局限
        •    文档型数据库
                相关产品CouchDB、MongoDB
                典型应用Web应用与Key-Value类似Value是结构化的
                数据模型 一系列键值对
                优势数据结构要求不严格
                劣势 查询性能不高而且缺乏统一的查询语法
        •    图形(Graph)数据库
                相关数据库Neo4J、InfoGrid、Infinite Graph
                典型应用社交网络
                数据模型图结构
                优势利用图结构相关算法。
                劣势需要对整个图做计算才能得出结果不容易做分布式的集群方案。
    1.3 什么是Redis
        Redis是用C语言开发的一个开源的高性能键值对key-value数据库官方提供测试数据50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s 且Redis通过提供多种键值数据类型来适应不同场景下的存储需求目前为止Redis支持的键值数据类型如下
            1) 字符串类型 string
            2) 哈希类型 hash
            3) 列表类型 list
            4) 集合类型 set
            5) 有序集合类型 sortedset
        1.3.1 redis的应用场景
            •    缓存数据查询、短连接、新闻内容、商品内容等等
            •    聊天室的在线好友列表
            •    任务队列。秒杀、抢购、12306等等
            •    应用排行榜
            •    网站访问统计
            •    数据过期处理可以精确到毫秒
            •    分布式集群架构中的session分离

2. 下载安装
    1. 官网https://redis.io
    2. 中文网http://www.redis.net.cn/
    3. 解压直接可以使用
        * redis.windows.conf配置文件
        * redis-cli.exeredis的客户端
        * redis-server.exeredis服务器端
打开redis-server.exe后,打开 redis-cli.exe
    
3. 命令操作
    1. redis的数据结构
        * redis存储的是key,value格式的数据其中key都是字符串value有5种不同的数据结构
            * value的数据结构
                1) 字符串类型 string
                2) 哈希类型 hash map格式  
                3) 列表类型 list linkedlist格式。支持重复元素
                4) 集合类型 set   不允许重复元素
                5) 有序集合类型 sortedset不允许重复元素且元素有顺序

    
    2. 字符串类型 string
        1. 存储 set key value
            127.0.0.1:6379> set username zhangsan
            OK
        2. 获取 get key
            127.0.0.1:6379> get username
            "zhangsan"
        3. 删除 del key
            127.0.0.1:6379> del age
            (integer) 1

    3. 哈希类型 hash
        1. 存储 hset key field value
            127.0.0.1:6379> hset myhash username lisi
            (integer) 1
            127.0.0.1:6379> hset myhash password 123
            (integer) 1
        2. 获取 
            * hget key field: 获取指定的field对应的值
                127.0.0.1:6379> hget myhash username
                "lisi"
            * hgetall key获取所有的field和value
                127.0.0.1:6379> hgetall myhash
                1) "username"
                2) "lisi"
                3) "password"
                4) "123"
                
        3. 删除 hdel key field
            127.0.0.1:6379> hdel myhash username
            (integer) 1

    
    4. 列表类型 list:可以添加一个元素到列表的头部左边或者尾部右边

        1. 添加
            1. lpush key value: 将元素加入列表左表
                
            2. rpush key value将元素加入列表右边
                
                127.0.0.1:6379> lpush myList a
                (integer) 1
                127.0.0.1:6379> lpush myList b
                (integer) 2
                127.0.0.1:6379> rpush myList c
                (integer) 3
        2. 获取
            * lrange key start end 范围获取
                127.0.0.1:6379> lrange myList 0 -1
                1) "b"
                2) "a"
                3) "c"
        3. 删除
            * lpop key 删除列表最左边的元素并将元素返回
            * rpop key 删除列表最右边的元素并将元素返回

 

 5. 集合类型 set 不允许重复元素
        1. 存储sadd key value
            127.0.0.1:6379> sadd myset a
            (integer) 1
            127.0.0.1:6379> sadd myset a
            (integer) 0
        2. 获取smembers key:获取set集合中所有元素
            127.0.0.1:6379> smembers myset
            1) "a"
        3. 删除srem key value:删除set集合中的某个元素    
            127.0.0.1:6379> srem myset a
            (integer) 1

    6. 有序集合类型 sortedset不允许重复元素且元素有顺序.每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

        1. 存储zadd key score value
            127.0.0.1:6379> zadd mysort 60 zhangsan
            (integer) 1
            127.0.0.1:6379> zadd mysort 50 lisi
            (integer) 1
            127.0.0.1:6379> zadd mysort 80 wangwu
            (integer) 1
        2. 获取zrange key start end [withscores]
            127.0.0.1:6379> zrange mysort 0 -1
            1) "lisi"
            2) "zhangsan"
            3) "wangwu"

            127.0.0.1:6379> zrange mysort 0 -1 withscores
            1) "zhangsan"
            2) "60"
            3) "wangwu"
            4) "80"
            5) "lisi"
            6) "500"
        3. 删除zrem key value
            127.0.0.1:6379> zrem mysort lisi
            (integer) 1

    7. 通用命令
        1. keys * : 查询所有的键
        2. type key 获取键对应的value的类型
        3. del key删除指定的key value

 4. 持久化
    1. redis是一个内存数据库当redis服务器重启获取电脑重启数据会丢失我们可以将redis内存中的数据持久化保存到硬盘的文件中。
    2. redis持久化机制
        1. RDB默认方式不需要进行配置默认就使用这种机制
            * 在一定的间隔时间中检测key的变化情况然后持久化数据
            1. 编辑redis.windwos.conf文件
                #   after 900 sec (15 min) if at least 1 key changed
                save 900 1
                #   after 300 sec (5 min) if at least 10 keys changed
                save 300 10
                #   after 60 sec if at least 10000 keys changed
                save 60 10000
                
            2. 重新启动redis服务器并指定配置文件名称
                D:\JavaWeb2018\day23_redis\资料\redis\windows-64\redis-2.8.9>redis-server.exe redis.windows.conf    
            
        2. AOF日志记录的方式可以记录每一条命令的操作。可以每一次命令操作后持久化数据
            1. 编辑redis.windwos.conf文件
                appendonly no关闭aof --> appendonly yes 开启aof
                
                # appendfsync always 每一次操作都进行持久化
                appendfsync everysec 每隔一秒进行一次持久化
                # appendfsync no     不进行持久化

5. Java客户端 Jedis

    * Jedis: 一款java操作redis数据库的工具.
    * 使用步骤
        1. 下载jedis的jar包
jedis-2.7.0.jar
commons-pool2-2.3.jar
 

import cn.itcast.jedis.util.JedisPoolUtils;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * jedis的测试类
 */
public class JedisTest {
    /**
     * 快速入门
     */
    @Test
    public void test1(){
        //1. 获取连接
        Jedis jedis = new Jedis("localhost",6379);
        //2. 操作
        jedis.set("username","zhangsan");
        System.out.println(jedis.get("username"));;
        //3. 关闭连接
        jedis.close();
    }
    /**
     * string 数据结构操作
     */
    @Test
    public void test2(){
        //1. 获取连接
        Jedis jedis = new Jedis();//如果使用空参构造默认值 "localhost",6379端口
        //2. 操作
        //存储
        jedis.set("username","zhangsan");
        //获取
        String username = jedis.get("username");
        System.out.println(username);

        //可以使用setex()方法存储可以指定过期时间的 key value
        jedis.setex("activecode",20,"hehe");//将activecodehehe键值对存入redis并且20秒后自动删除该键值对

        //3. 关闭连接
        jedis.close();
    }
    /**
     * hash 数据结构操作
     */
    @Test
    public void test3(){
        //1. 获取连接
        Jedis jedis = new Jedis();//如果使用空参构造默认值 "localhost",6379端口
        //2. 操作
        // 存储hash
        jedis.hset("user","name","lisi");
        jedis.hset("user","age","23");
        jedis.hset("user","gender","female");

        // 获取hash
        String name = jedis.hget("user", "name");
        System.out.println(name);
        // 获取hash的所有map中的数据
        Map<String, String> user = jedis.hgetAll("user");

        // keyset
        Set<String> keySet = user.keySet();
        for (String key : keySet) {
            //获取value
            String value = user.get(key);
            System.out.println(key + ":" + value);
        }

        //3. 关闭连接
        jedis.close();
    }
    /**
     * list 数据结构操作
     */
    @Test
    public void test4(){
        //1. 获取连接
        Jedis jedis = new Jedis();//如果使用空参构造默认值 "localhost",6379端口
        //2. 操作
        // list 存储
        jedis.lpush("mylist","a","b","c");//从左边存
        jedis.rpush("mylist","a","b","c");//从右边存

        // list 范围获取
        List<String> mylist = jedis.lrange("mylist", 0, -1);
        System.out.println(mylist);
        
        // list 弹出
        String element1 = jedis.lpop("mylist");//c
        System.out.println(element1);

        String element2 = jedis.rpop("mylist");//c
        System.out.println(element2);

        // list 范围获取
        List<String> mylist2 = jedis.lrange("mylist", 0, -1);
        System.out.println(mylist2);

        //3. 关闭连接
        jedis.close();
    }
    /**
     * set 数据结构操作
     */
    @Test
    public void test5(){
        //1. 获取连接
        Jedis jedis = new Jedis();//如果使用空参构造默认值 "localhost",6379端口
        //2. 操作


        // set 存储
        jedis.sadd("myset","java","php","c++");

        // set 获取
        Set<String> myset = jedis.smembers("myset");
        System.out.println(myset);

        //3. 关闭连接
        jedis.close();
    }
    /**
     * sortedset 数据结构操作
     */
    @Test
    public void test6(){
        //1. 获取连接
        Jedis jedis = new Jedis();//如果使用空参构造默认值 "localhost",6379端口
        //2. 操作
        // sortedset 存储
        jedis.zadd("mysortedset",3,"亚瑟");
        jedis.zadd("mysortedset",30,"后裔");
        jedis.zadd("mysortedset",55,"孙悟空");

        // sortedset 获取
        Set<String> mysortedset = jedis.zrange("mysortedset", 0, -1);

        System.out.println(mysortedset);
        //3. 关闭连接
        jedis.close();
    }
    /**
     * jedis连接池使用
     */
    @Test
    public void test7(){

        //0.创建一个配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(50);
        config.setMaxIdle(10);

        //1.创建Jedis连接池对象
        JedisPool jedisPool = new JedisPool(config,"localhost",6379);

        //2.获取连接
        Jedis jedis = jedisPool.getResource();
        //3. 使用
        jedis.set("hehe","heihei");
        System.out.println(jedis.get("hehe"));

        //4. 关闭 归还到连接池中
        jedis.close();;
    }
    /**
     * jedis连接池工具类使用
     */
    @Test
    public void test8(){
        //通过连接池工具类获取
        Jedis jedis = JedisPoolUtils.getJedis();
        //3. 使用
        jedis.set("hello","world");
        System.out.println(jedis.get("hello"));

        //4. 关闭 归还到连接池中
        jedis.close();;
    }
}

* jedis连接池 JedisPool
    * 连接池工具类

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 JedisPool工具类
    加载配置文件配置连接池的参数
    提供获取连接的方法

 */
public class JedisPoolUtils {

    private static JedisPool jedisPool;

    static{
        //读取配置文件
        InputStream is = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
        //创建Properties对象
        Properties pro = new Properties();
        //关联文件
        try {
            pro.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取数据设置到JedisPoolConfig中
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
        config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));

        //初始化JedisPool
        jedisPool = new JedisPool(config,pro.getProperty("host"),Integer.parseInt(pro.getProperty("port")));
    }
    /**
     * 获取连接方法
     */
    public static Jedis getJedis(){
        return jedisPool.getResource();
    }
}

jedis.properties

host=127.0.0.1
port=6379
maxTotal=50
maxIdle=10


案例

案例需求
    1. 提供index.html页面页面中有一个省份 下拉列表
    2. 当 页面加载完成后 发送ajax请求加载所有省份

* 注意使用redis缓存一些不经常发生变化的数据。
    * 数据库的数据一旦发生改变则需要更新缓存。
        * 数据库的表执行 增删改的相关操作需要将redis缓存数据情况再次存入
        * 在service对应的增删改方法中将redis数据删除。

CREATE DATABASE day23; -- 创建数据库
USE day23;                -- 使用数据库
CREATE TABLE province(   -- 创建表
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20) NOT NULL
    
);
-- 插入数据
INSERT INTO province VALUES(NULL,'北京');
INSERT INTO province VALUES(NULL,'上海');
INSERT INTO province VALUES(NULL,'广州');
INSERT INTO province VALUES(NULL,'陕西');
依赖
jquery-3.3.1.js
jquery-3.3.1.min.js
commons-beanutils-1.8.3.jar
commons-logging-1.1.1.jar
commons-pool2-2.3.jar
druid-1.0.9.jar
jackson-annotations-2.2.3.jar
jackson-core-2.2.3.jar
jackson-databind-2.2.3.jar
javax.servlet.jsp.jstl.jar
jedis-2.7.0.jar
jstl-impl.jar
mysql-connector-java-5.1.18-bin.jar
mysql-connector-java-8.0.18.jar
spring-beans-4.2.4.RELEASE.jar
spring-core-4.2.4.RELEASE.jar
spring-jdbc-4.2.4.RELEASE.jar
spring-tx-4.2.4.RELEASE.jar


       druid.properties

#driverClassName=com.mysql.jdbc.Driver
driverClassName=com.mysql.cj.jdbc.Driver
#url=jdbc:mysql:///day23
url=jdbc:mysql:///db?serverTimezone=UTC
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000

JDBCUtils

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * JDBC工具类 使用Durid连接池
 */
public class JDBCUtils {

    private static DataSource ds ;

    static {

        try {
            //1.加载配置文件
            Properties pro = new Properties();
            //使用ClassLoader加载配置文件获取字节输入流
            InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
            pro.load(is);

            //2.初始化连接池对象
            ds = DruidDataSourceFactory.createDataSource(pro);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接池对象
     */
    public static DataSource getDataSource(){
        return ds;
    }


    /**
     * 获取连接Connection对象
     */
    public static Connection getConnection() throws SQLException {
        return  ds.getConnection();
    }
}

具体类

public class Province {

    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Dao接口
 

import cn.itcast.domain.Province;

import java.util.List;

public interface ProvinceDao {

    public List<Province> findAll();
}

Dao实现类

import cn.itcast.dao.ProvinceDao;
import cn.itcast.domain.Province;
import cn.itcast.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

public class ProvinceDaoImpl implements ProvinceDao {

    //1.声明成员变量 jdbctemplement
    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());

    @Override
    public List<Province> findAll() {
        //1.定义sql
        String sql = "select * from province ";
        //2.执行sql
        List<Province> list = template.query(sql, new BeanPropertyRowMapper<Province>(Province.class));
        return list;
    }
}

Service接口


import cn.itcast.domain.Province;

import java.util.List;

public interface ProvinceService {

    public List<Province> findAll();

    public String findAllJson();
}

Service实现类

import cn.itcast.dao.ProvinceDao;
import cn.itcast.dao.impl.ProvinceDaoImpl;
import cn.itcast.domain.Province;
import cn.itcast.jedis.util.JedisPoolUtils;
import cn.itcast.service.ProvinceService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import redis.clients.jedis.Jedis;

import java.util.List;

public class ProvinceServiceImpl implements ProvinceService {
    //声明dao
    private ProvinceDao dao = new ProvinceDaoImpl();

    @Override
    public List<Province> findAll() {
        return dao.findAll();
    }

    /**
        使用redis缓存
     */

    @Override
    public String findAllJson() {
        //1.先从redis中查询数据
        //1.1获取redis客户端连接
        Jedis jedis = JedisPoolUtils.getJedis();
        String province_json = jedis.get("province");

        //2判断 province_json 数据是否为null
        if(province_json == null || province_json.length() == 0){
            //redis中没有数据
            System.out.println("redis中没数据查询数据库...");
            //2.1从数据中查询
            List<Province> ps = dao.findAll();
            //2.2将list序列化为json
            ObjectMapper mapper = new ObjectMapper();
            try {
                province_json = mapper.writeValueAsString(ps);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }

            //2.3 将json数据存入redis
            jedis.set("province",province_json);
            //归还连接
            jedis.close();

        }else{
            System.out.println("redis中有数据查询缓存...");
        }


        return province_json;
    }
}

Servlet

import cn.itcast.service.ProvinceService;
import cn.itcast.service.impl.ProvinceServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/provinceServlet")
public class ProvinceServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       /* //1.调用service查询
        ProvinceService service = new ProvinceServiceImpl();
        List<Province> list = service.findAll();
        //2.序列化list为json
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(list);*/

        //1.调用service查询
        ProvinceService service = new ProvinceServiceImpl();
        String json = service.findAllJson();


        System.out.println(json);
        //3.响应结果
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(json);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

HTML页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    
    <script src="js/jquery-3.3.1.min.js"></script>
    
    <script>
        $(function () {
           
            //发送ajax请求加载所有省份数据
            $.get("provinceServlet",{},function (data) {
                //[{"id":1,"name":"北京"},{"id":2,"name":"上海"},{"id":3,"name":"广州"},{"id":4,"name":"陕西"}]

                //1.获取select
                var province = $("#province");
                //2.遍历json数组
                $(data).each(function () {
                    //3.创建<option>
                    var option = "<option name='"+this.id+"'>"+this.name+"</option>";

                    //4.调用select的append追加option
                    province.append(option);
                });


            });
            
        });
        
        
    </script>
    
</head>
<body>



        <select id="province">
            <option>--请选择省份--</option>

        </select>
</body>
</html>

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: redis数据库