精华内容
下载资源
问答
  • 1.什么是Redis Redis一个完全免费开源的 NoSQL数据库 由意大利人开发的一款内存高速缓存数据库 该软件由C语言编写,数据模型为 Key Value 它支持丰富的数据结构(类型),比如String list hash set sorted.可持久...

    1.什么是Redis

    image
    Redis是一个完全免费开源的 NoSQL数据库 是由意大利人开发的一款内存高速缓存数据库 该软件由C语言编写,数据模型为
    Key Value 它支持丰富的数据结构(类型),比如String list hash set sorted.可持久化,保证了数据安全。
    用处:
    经常用在 热点数据 经常会被查询,但是不经常被修改删除的数据

    官方测试数据

    • 读的速度 110000次/s
    • 写的速度 80000次/s
    • 不会存在线程安全的问题
    • 默认支持16个数据库

    image

    缺点
    太耗内存


    NoSQL 非关系型数据库

    NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL"。
    在现代的计算系统上每天网络上都会产生庞大的数据量。

    这些数据有很大一部分是由关系数据库管理系统(RDBMS)来处理。 1970年 E.F.Codd’s提出的关系模型的论文 “A relational model of data for large shared data banks”,这使得数据建模和应用程序编程更加简单。
    使用需求

    • 支持大量并发用户(数万,甚至数百万量级)
    • 为全球范围内的用户提供高质量的交互体验
    • 高可用性 高并发
    • 支持处理半结构化数据和非结构化数据
    • 快速适应不断变化的需求

    (DBMS)关系型数据库
    MySQL SQLServer Oracle 等等

    (NoSQL)非关系型数据库
    MongoDB Redis 等等


    安装Redis数据库

    • 连接互联网 更新软件包
    sudo apt-get update
    
    • 输入 执行后
    sudo  apt-get install -y redis-server
    
    • 出现错误时尝试解决apt依赖

    • 默认安装路径为 /usr/bin里 可使用 ls redis* 查看

    • 输入redis-cli 启动redis客户端

    # 启动Redis客户端
    redis-cli 
    
    # 客户端命令 
    # help 查看Redis版本信息等
    help
    # 退出Redis客户端
    quit 或 exit
    

    image

    • 输入 ping 测试是否成功
    • 出现PONG表示畅通
    ping
    

    image

    • 通过启动命令检查Redis服务器状态默认端口号6379
    netstat -nlt|grep 6379
    
    • 配置文件默认在 /etc/redis/redis.conf

    修改配置文件 配置文件较为长 可以使用 vivim 的搜索功能进行查找

    vivim 的搜索功能教程地址

    1 .用vi打开Redis服务器的配置文件redis.conf

    # 进入Redis配置文件
    vi /etc/redis/redis.conf
    

    Redis以守护进程运行

    • 如果以守护进程运行,则不会在命令行阻塞,类似于服务
    • 如果以非守护进程运行,则当前终端被阻塞,无法使用
    • 推荐改为yes,以守护进程运行
    • 进入配置文件找到这一行 修改为以下配置
    daemonize yes
    

    image
    2 .修改访问Redis的密码

    使用Java连接Redis必须添加密码!!!!

    #取消注释requirepass 添加你的密码
    requirepass your_password
    

    image

    3 .让Redis服务器被远程访问

    默认情况下,Redis服务器不允许远程访问,只允许本机访问,所以我们需要设置打开远程访问的功能。

    配置文件中有好几处 类似的地方 不要找错了
    配置文件里有好几处bind 127.0.0.1 不要找错了!!!!!!
    大概在69行左右

    #注释bind
    #bind 127.0.0.1
    

    image

    修改后,重启Redis服务器。

    service redis-server restart
    

    未使用密码登陆Redis服务器

    他会告诉你 没有权限不能访问

    redis-cli
    redis 127.0.0.1:6379> keys *
    (error) ERR operation not permitted
    

    **登陆Redis服务器,输入密码 **

    这时候就能正常的访问了

    redis-cli -a your_password
    redis 127.0.0.1:6379> keys *
    1) "name"
    2)....你的所有key
    

    配置完成后重新启动服务器

    # 重启redis-server 的三个命令 任选其一即可
    sudo service redis-server restart
    
    sudo /etc/init.d/redis-server restart
    
    sudo redis-server /etc/redis/redis.conf
    

    Redis关闭

    ./redis-cli shutdown 正常方式关闭 Redis 会进行持久化操作
    kill -9 进程号 强制关闭Redis服务端 不会进行持久化操作 容易造成数据丢失
    ps -ef | grep -i redis 查看当前Redis运行的进程号
    如果想要保证数据不造成丢失可以修改Redis配置文件的save 持久化时间

    Ubuntu中使用Redis客户端

    服务端设置密码后 客户端的连接方法必须带参数 ./redis-cli -h host -p port -a password

    redis-cli -h 192.168.0.100 -p 6379 -a 123456
    
    • host代表 连接的IP地址
    • port 连接的端口号
    • password 密码
      本机(虚拟机本机)访问可以忽略IP地址 port忽略的话默认是6379

    Redis客户端常用命令 String

    命令 作用 示例
    del key 根据key删除 del userName 或 del key1 key2 …
    keys pattern 通配符 *代表所有 user? 开头为user的key key * 或 key user?
    exists key 判断一个key是否存在 返回值0或1
    expire key seconds 给一个key设置过期时间 单位:秒
    ttl key 查询 kye的生命周期 返回值 -1:永久 -2:无效
    persist key 移除key的过期时间设置为永久有效
    select index 切换到第index个数据库 select 1 切换到第一个数据库
    rename key newName 修改key的名字 rename key1 user1
    move key index 把key移动到下标为index的数据库里去 move user1 1 把user1移动到下标为1的数据库里
    type key 查看key是什么类型
    FLUSHALL 删除所有数据库中所有的key 用于辞职时 删库跑路
    FLUSHDB 删除当前数据库中所有的key

    Key的命名规范

    key区分大小写 命令不区分大小写

    • 单个Key可以支持512M的大小
    • key不要太长尽量不能超过1024字节 不仅降低内存 还增长查询时间
    • key也不要太短,太短的话key的可读性会降低
    • 在一个项目中key的命名规范尽量使用统一的命名格式 列如:user12
    • 对应到表上就是 user表有 id,name,age…属性 命名格式就可以是
      • user:1:name 或 user:2:name 中间的数字为用户的id 用于区分数据
      • : 号 为程序员的统一规范 其他的符号也可以用

    Redis的数据类型

    • string 是redis的最基本的数据类型 是二进制安全的 一个key对一个value 单个key可以最大存储512M的数据
    • 二进制安全特点
      • 编码 解码在客户端进行 执行效率高
      • 不需要频繁编码解码,不会出现乱码

    string类型常用命令

    命令 作用 示例
    set Key 创建一个Key set key1
    set Key value 创建一个Key且赋值 set key1 123
    mset k1 v1 k2 v2… 存多个key value
    mget k1 k2 取多个key
    setnx key value 当key不存在时才创建 key存在时返回0
    setex key seconds value 将键 key 的值设置为 value , 并将键 key 的生存时间设置为 seconds 秒钟。如果键 key 已经存在, 那么 SETEX 命令将覆盖已有的值。
    getrange key start end 根据下标取值 getrange key 0 3 返回下标0到3的值
    strlen key 查看字符串长度
    incr key 每次自增key的值+1 decr key 自减
    incrby key 增量值 每次自增key的增量值 decyby key 减值
    get keys 根据Key获取Value get userName 返回nil 就是null
    • 应用场景

    • 保存单个字符或字符串或JSON格式的数据
    • 计数器 点击量 粉丝数等等
    • incr等指令本身具有原子性操作的特性所有完全可以用redis的 自增自减操作实现计数的效果 假如有5个用户同时点击那么 就要排队最后的值一定是5

    Hash类型

    简介:RedisHash是一个string类型的field 和value的影视表,特别适合存储对象。每个hash可以存储(40多亿)个键值对,而且占用很少磁盘空间

    常用命令

    命令 作用 示例
    hset key field value 赋值语法 为指定的key设置fild和value hset users:1 uname 张三
    hmset key field value field value… 赋值语法 为指定的key设置fild和value hmset users:1 uname 张三 uage 24 … 此命令会覆盖哈希表中已存在的域。
    hget key field 取值语法 指定key 根据field取得value hget users:1 uname
    hmget key field1 field2 … 取多值语法 指定多个field获取多个value hmget users:1 uname age …
    hgetall key 指定key获取key中所有的value hgetall users:1
    hkeys key 指定key获取key中所有的 field名称 hkeys users:1
    hlen key 获取key中的一共有多少个filed
    hlen key 获取key中有多少个field
    hdel key1 key2 删除指定的key
    hincrby key field increment 给key中为数值的field增量 hincrby users:1 age 10
    hincrbyfloat key filed increment 给key中为浮点数的field增量 hincrby users:1 money 10.1
    hexists key field 查看key中的field是否存在 返回1或0
    hsetnx key field 当field 尚未存在于哈希表的情况下, 将它的值设置为 value 如果哈希表 hash 不存在, 那么一个新的哈希表将被创建并执行 hsetnx 命令。
    hstrlen key field 返回哈希表 key 中,field 值的字符串长度。 hstrlen myhash f1
    • 应用场景

    • 存储对象 Hash是最接近关系型数据库的类型
    • 不允许没有field为空的数据存在 如果一个key的field被删完了那么这个key就会被redis回收
      image

    list列表常用命令

    命令 作用 示例
    lpush key value [value…] 将一个或多个值 value 插入到列表 key 的表头 LPUSH a b c 先进后出 返回的就是 c b a值可以重复
    lpushx key value [value…] 当key存在时才添加否则什么都不做 功能与lpush一致
    rpush key value[value…] 往表尾部添加数据 与lpush刚好相反 插入a b c 返回 a b c
    rpushx key value [value…] 当key存在时才添加否则什么都不做 功能与lpushx刚好相反
    lpop key 移除头元素第一个元素
    rpop key 与上相反
    lrem key count value 根据位置移除key lrem greet 2 morning移除从表头到表尾,最先发现的2个 morning 元素被移除
    llen key 查询key的长度
    lindex key index 返回下表时index的元素 没找到返回nil
    lset key index value 把下表为index的key赋值 用该命令操作空列表(报错)索引超出列表长度(报错)
    lrange key start end 区间查询 LRANGE fp-language 0 1 0 -1代表查询所有
    ltrim key start end 区间删除 LTRIM alpha 1 -1删除下标为1到-1之间的key(下标0)
    BLPOP key [key …] timeout 阻塞查询
    BLPOP job command 300 当key不存在时会被阻塞,直到另一客户端进行添加操作300为等待秒数 如果超时还没有查到那么返回nil
    RPOPLPUSH 集合1 集合2 把第一个集合的第一个元素(RPOP)移除 并且添加到第二个集合里边(lpush添加) 也可以把当前集合的最后一个元素换位到当前集合第一个位置(rpoplpush 集合1 集合1)

    应用场景

    • 实现消息队列的功能 不必用MySQL的order by排序
    • 分页
    • 任务队列

    set集合常用命令 无序

    命令 作用 示例
    sadd key value [value…] 将一个或多个元素添加到集合中 当key不是集合类型的时候则会报错当key不存在则新创建包含value的集合
    sismember key value 判断key中是否包含该value
    spop key 移除key中随机一个元素并返回
    srandmember key count 随机移除count个元素并返回 当count没有指定就移除一个元素并返回
    srem key value 移除key中指定的value 当key不是集合类型报错
    smove key newkey value 把一个key中的指定value移动到新的key中
    scard key 返回key中元素的数量
    smembers key 返回可以中所有的成员
    sinter key [key…] 返回多个集合中的交集 key1 1 2 3 key2 2 3 4 返回值 2 3 交集
    sinterstore newKey[key…] 把多个key中的交集保存到新的key中
    sunion key [key…] 返回多个集合中的并集 返回两个集合中所有的value
    SUNIONSTORE newKey [key…] 把多个集合的并集保存到新的集合
    sdiff key [key…] 返回多个集合中的差集 SDIFF peter’s_movies joe’s_movies 左边的为主集合以主集合为基准返回值就是 主集合中有的 副集合里没有的数据
    SDIFFSTORE newKey [key…] 把多个集合的差集保存到新集合中 交并差集解释

    set集合常用命令 有序

    命令 作用 示例

    SpringBoot整合Redis

    • 在pom.xml中添加依赖
    # 此依赖被Spring官方收录在spring-boot-starter-parent中有版本号
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--lettuce pool连接池-->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    
    • 在application.yml中添加配置
    spring:
      redis:
        host: 192.168.244.102 # Redis服务器地址
        port: 6379 # Redis服务器连接端口
        password: root # Redis服务器连接密码
        database: 0 # Redis数据库索引(默认为0)
        lettuce:
          shutdown-timeout: 0
          pool:
            max-active: 8 # 连接池最大连接数(使用负值表示没有限制) 默认 8 
            max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
            max-idle: 8 # 连接池中的最大空闲连接 默认 8
            min-idle: 0 # 连接池中的最小空闲连接 默认 0
    
    
    • 上文配置的 jedis 和 lettuce是两种不同的Redis客户端

    • Jedis 是直连模式,在多个线程间共享一个 Jedis 实例时是线程不安全的,每个线程都去拿自己的 Jedis 实例,当连接数量增多时,物理连接成本就较高了。

    • 2、Lettuce的连接是基于Netty的,连接实例可以在多个线程间共享,如果你不知道Netty也没事,大致意思就是一个多线程的应用可以使用同一个连接实例,而不用担心并发线程的数量。通过异步的方式可以让我们更好地利用系统资源。

    编写RedisConfig

    • 完整的配置类
    package cn.yufire.redis.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import org.springframework.data.redis.support.collections.RedisCollectionFactoryBean;
    /**
     * 
     * <p>
     * Description:
     * </p>
     *  RedisConfig的配置类
     * @author yufire
     * @version v1.0.0
     * @since 2020-02-21 21:37:46
     * @see cn.yufire.redis.config
     *
     */
    @Configuration
    public class RedisConfig {
    
        /**
         * 使用 LettuceConnectionFactory连接池
         * 也可以使用 JedisConnectionFactory 需要引入依赖和配置
         * 也可以使用 RedisConnectionFactory Redis默认的连接池
         * @param factory
         * @return
         */
        @Bean
        public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory factory){
    
            RedisTemplate<String,Object> template = new RedisTemplate<String,Object>();
            template.setConnectionFactory(factory);
            return template;
    
        }
    
    }
    
    
            //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
            Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
    
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            // key采用String的序列化方式
            template.setKeySerializer(stringRedisSerializer);
            // hash的key也采用String的序列化方式
            template.setHashKeySerializer(stringRedisSerializer);
            // value序列化方式采用jackson
            template.setValueSerializer(jacksonSeial);
            // hash的value序列化方式采用jackson
            template.setHashValueSerializer(jacksonSeial);
            template.afterPropertiesSet();
    

    可以去网上找一个完整的RedisUtil工具类 也可以使用redisTemplate进行操作


    使用redisTemplate操作Redis

    //注入RedisTemplate模板
    @Autowired
    private RedisTemplate<String,String > redisTemplate;
    
    //测试插入一个对象到Redis
    @Test
    public void userAddToRedis() throws JsonProcessingException {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        user.setPassword("123456");
        user.setAge(18);
    
        ObjectMapper mapper = new ObjectMapper();
        //使用Jackosn把user对象转成json类型存入Redis
        String userJson = mapper.writeValueAsString(user);
        //存入Redis
        redisTemplate.opsForValue().set("user",userJson);
    
        //从Redis取出User的json串
        String redisGetUserJson = redisTemplate.opsForValue().get("user");
    
        //使用Jackson把json字符串转换为User对象
        User redisGetUser = mapper.readValue(redisGetUserJson, User.class);
    
        //打印
        System.out.println(redisGetUser);
    
    }
    
    
    • 控制台输出

    image

    • redisTemplate方法说明
    redisTemplate.opsForValue(); //操作字符串类型
    redisTemplate.opsForHash();  //操作hash类型
    redisTemplate.opsForList();  //操作List类型
    redisTemplate.opsForSet();   //操作Set类型
    redisTemplate.opsForZSet();  //操作有序set类型
    
    具体使用的时候,你可以根据自己的数据类型选择相应的方法即可,网上有各种RedisUtil工具类。

    普通Java项目连接Redis

    添加Maven依赖
    <!--Redis客户端的 使用连接池需要添加commons-pool2的jar redis客户端提供依赖所以不用写-->
    <dependency>
          <groupId>redis.clients</groupId>
          <artifactId>jedis</artifactId>
          <version>2.8.1</version>
    </dependency>
    <!--spring-data-redis Spring提供的RedisTemplate模板-->
    <dependency>
          <groupId>org.springframework.data</groupId>
          <artifactId>spring-data-redis</artifactId>
          <version>1.6.4.RELEASE</version>
    </dependency>
    <!--与spring整合的spring核心包-->
    <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
          <version>4.3.20.RELEASE</version>
    </dependency>
    

    单实例连接Redis

    		Jedis jedis = new Jedis("192.168.189.132",6379);//ip 和 端口号
    		jedis.auth("123456");//密码 Redis服务器必须设置密码
    		/**
    		 * 测试是否连接成功成功返回 PONG
    		 * 如果显示连接超时 检查linux服务器是否开放6379端口
    		 */
    		System.out.println(jedis.ping());
    

    连接池连接Redis

            /**
    		 * 连接池连接  帮助我们管理连接
    		 */
    		//获取连接池配置对象设置配置信息
    		JedisPoolConfig config = new JedisPoolConfig();
    		//设置最大连接数
    		config.setMaxTotal(30);
    		//最大空闲数
    		config.setMaxIdle(10);
    		
    		//获得连接池
    		JedisPool jedisPool = new JedisPool(config,"192.168.189.132",6379);
    		
    		//获得核心对象
    		Jedis jedis2 = null;
    		
    		try {
    			jedis2 = jedisPool.getResource();
    			jedis2.auth("123456"); //指定密码
    			/**
        		 * get set 操作
        		 * jedis2.get(key);....
    			 */
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			if (jedis2!=null) {
    				jedis2.close();
    			}
    			//虚拟机  Redis服务器关闭时  释放pool资源
    			if (jedisPool !=null) {
    				jedisPool.close();
    			}
    		}
    

    整合Spring后使用RedisTemplate模板连接Redis

    整合Spring的配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	   xmlns:context="http://www.springframework.org/schema/context"
    	   xmlns:aop="http://www.springframework.org/schema/aop"
    	   xmlns:tx="http://www.springframework.org/schema/tx"
    	   xsi:schemaLocation="http://www.springframework.org/schema/beans
    		http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context
    		http://www.springframework.org/schema/context/spring-context.xsd
    		http://www.springframework.org/schema/aop
    		http://www.springframework.org/schema/aop/spring-aop.xsd
    		http://www.springframework.org/schema/tx
    		http://www.springframework.org/schema/tx/spring-tx.xsd">
    
        <!--开启注解扫描-->
    	<context:component-scan base-package="com.sjy.redis"/>
    
    	<!-- 配置Redis配置 -->
    	<bean id="jedisConfig" class="redis.clients.jedis.JedisPoolConfig">
    		<!-- 最大连接数 -->
    		<property name="maxTotal" value="50"/>
    		<!-- 最大空闲数 -->
    		<property name="maxIdle" value="5"/>
    		<!-- 最大等待时间 -->
    		<property name="maxWaitMillis" value="300" />
    	</bean>
    	<!-- Spring整合Redis -->
    	<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    		<!-- IP地址 -->
    		<property name="hostName" value="192.168.189.132" />
    		<!-- 端口号 -->
    		<property name="port" value="6379" />
    		<property name="password" value="123456" />
    		<!-- 连接池配置引用 -->
    		<property name="poolConfig" ref="jedisConfig" />
    		<!-- 超时时间 默认2000-->
    		<property name="timeout" value="2000" />
    		<!-- usePool:是否使用连接池 -->
    		<property name="usePool" value="true"/>
    	</bean>
    
    	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    		<property name="ConnectionFactory" ref="jedisConnectionFactory"/>
    		<!--不让Jdk进行序列化操作-->
    		<property name="keySerializer">
    			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
    		</property>
    		<property name="valueSerializer">
    			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
    		</property>
    		<property name="hashKeySerializer">
    			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
    		</property>
    		<property name="hashValueSerializer">
    			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
    		</property>
    	</bean>
    
    </beans>
    
    测试Spring整合Redis
    
         //运行spring配置文件  启动spring
            ApplicationContext ac = new ClassPathXmlApplicationContext("spring_redis.xml");
    
            RedisTemplate<String ,String> template = (RedisTemplate<String, String>) ac.getBean("redisTemplate");
    
            //string类型的操作
            ValueOperations<String, String> string = template.opsForValue();
                            //还有其他类型          //Hash类型的   opsForHash()
                                                    //list类型的   opsForList()
                                                    
            
    

    Spring整合Redis需要指定是否序列化key value 否则会出现新增key时key的前边出现序列化的字符串导致出现问题


    Redis发送订阅


    Redis多数据库

    常用命令

    • Redis下数据库是由一个整数索引标识,
    • 而不是一个数据库名称。
    • 默认情况下连接到表示为0的数据库
    • redis配置文件redis.conf 中datebases 16 来控制数据库总数
    命令 作用 示例
    select index 切换到第index个数据库 select 1 切换到第一个数据库
    move key db 把指定的key移动到指定的数据库中 move user 2

    Redis事务

    • redis事务可以一次执行多个命令(按顺序的串行化执行,执行中不会被其他命令插入,不能加塞)
    • 当事务块中的某个命令出错时只有出错的命令不会执行,其他的命令继续执行,不会回滚
    • 如果编辑事务块的时候出现了语法错误提交时整个事务快将执行失败Redis进行回滚

    Redis事务执行的三个阶段

    事务命令 作用 示例
    DISCARD 取消事务,放弃执行事务块中所有的指令
    EXEC 执行所有事务块中的命令
    MULTI 标记一个事务的开始
    UNWATCH 取消WATCH命令对所有key的监视
    WATCH key [key…] 监视一个或多个key如果在事务在执行前这个key被其他命令改动的话,事务将被打断
    图片示例 Redis事务示例

    Redis数据淘汰策略

    • 当内存不足时Redis会根据redis-conf配置文件中的缓存淘汰策略进行淘汰部分key,以保证写入成功
    • 如果无法淘汰key时,Redis将返回out of memory内存溢出的错误
      Redis数据淘汰策略

    Redis持久化

    • 方式一 RDB(默认)
      • RDB相当于快照,保存的一种状态。把几十G的数据创建一个几kb的快照
      • 默认的文件名叫 dump.rdb
        • 优点
          • 保存数据块 还原数据速度极快 他是以二进制的格式写的dump.rdb
          • 适用于灾难备份
        • 缺点
          • 不适合小内存的机器使用
        • 快照条件
          • Redis-cli服务正常关闭的时候
          • key满足配置文件中的持久化策略的时候
    • 方式二 AOF
      • Redis的AOF持久化机制

    Redis的与mysql的一致性

    展开全文
  • Redis数据库学习

    2020-06-08 15:21:31
    文章目录Redis是什么?它的优点有哪些?Redis在Java Web中的应用缓存高速读/写场合Redis基本安装和使用Window 下安装Linux 下安装Ubuntu 下安装在Java程序中使用Redis在Spring中使用RedisRedis的6种数据类型NoSQL和...

    文章目录


    在这里插入图片描述
    Redis 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是当前互联网世界最为流行的 NoSQL 数据库。

    Redis 开源免费,提供了 Java,C/C++,C#,PHP 等客户端,使用方便。主要应用于内容缓存和处理大量数据的高访问负载。

    Redis是什么?它的优点有哪些?

    Redis 具备一定持久层的功能,也可以作为一种缓存工具。对于 NoSQL 数据库而言,作为持久层,它存储的数据是半结构化的,这就意味着计算机在读入内存中有更少的规则,读入速度更快。

    对于那些结构化、多范式规则的数据库系统而言,它更具性能优势。作为缓存,它可以支持大数据存入内存中,只要命中率高,它就能快速响应,因为在内存中的数据读/写比数据库读/写磁盘的速度快几十到上百倍,其作用如图 1 所示。
    在这里插入图片描述
    数据库系统有更好的规范性和数据完整性,功能更强大,作为持久层更为完善,安全性更高。而 NoSQL 结构松散、不完整,功能有限,目前尚不具备取代数据库的实力,但是作为缓存工具,它的高性能、高响应等功能,使它成为一个很重要的工具。

    当前 Redis 已经成为了主要的 NoSQL 工具,其原因如下。
    1)响应快速
    Redis 响应非常快,每秒可以执行大约 110 000 个写入操作,或者 81 000 个读操作,其速度远超数据库。如果存入一些常用的数据,就能有效提高系统的性能。
    2)支持 6 种数据类型
    它们是字符串、哈希结构、列表、集合、可排序集合和基数。比如对于字符串可以存入一些 Java 基础数据类型,哈希可以存储对象,列表可以存储 List 对象等。这使得在应用中很容易根据自己的需要选择存储的数据类型,方便开发。

    对于 Redis 而言,虽然只有 6 种数据类型,但是有两大好处:一方面可以满足存储各种数据结构体的需要;另外一方面数据类型少,使得规则就少,需要的判断和逻辑就少,这样读/写的速度就更快。
    3)操作都是原子的
    所有 Redis 的操作都是原子的,从而确保当两个客户同时访问 Redis 服务器时,得到的是更新后的值(最新值)。在需要高并发的场合可以考虑使用 Redis 的事务,处理一些需要锁的业务。
    4)MultiUtility 工具
    Redis 可以在如缓存、消息传递队列中使用(Redis 支持“发布+订阅”的消息模式),在应用程序如 Web 应用程序会话、网站页面点击数等任何短暂的数据中使用。

    正是因为 Redis 具备这些优点,使得它成为了目前主流的 NoSQL 技术,在 Java 互联网中得到了广泛使用。

    一方面,使用 NoSQL 从数据库中读取数据进行缓存,就可以从内存中读取数据了,而不像数据库一样读磁盘。现实是读操作远比写操作要多得多,所以缓存很多常用的数据,提高其命中率有助于整体性能的提高,并且能减缓数据库的压力,对互联网系统架构是十分有利的。

    另一方面,它也能满足互联网高并发需要高速处理数据的场合,比如抢红包、商品秒杀等场景,这些场合需要高速处理,并保证并发数据安全和一致性。

    Redis在Java Web中的应用

    一般而言 Redis 在 Java Web 应用中存在两个主要的场景,一个是缓存常用的数据,另一个是在需要高速读/写的场合使用它快速读/写,比如一些需要进行商品抢购和抢红包的场合。

    由于在高并发的情况下,需要对数据进行高速读/写的场景,一个最为核心的问题是数据一致性和访问控制。

    缓存

    在对数据库的读/写操作中,现实的情况是读操作的次数远超写操作,一般是 1:9 到 3:7 的比例,所以需要读的可能性是比写的可能性多得多。

    当发送 SQL 去数据库进行读取时,数据库就会去磁盘把对应的数据索引回来,而索引磁盘是一个相对缓慢的过程。如果把数据直接放在运行在内存中的 Redis 服务器上,那么不需要去读/写磁盘了,而是直接读取内存,显然速度会快得多,并且会极大减轻数据库的压力。

    而使用内存进行存储数据开销也是比较大的,因为磁盘可以是 TGB 级别,而且十分廉价,内存一般是几百个 GB 就相当了不起了,所以内存虽然高效但空间有限,价格也比磁盘高许多,因此使用内存代价较高,并不是想存什么就存什么,因此我们应该考虑有条件的存储数据。

    一般而言,存储一些常用的数据,比如用户登录的信息;一些主要的业务信息,比如银行会存储一些客户基础信息、银行卡信息、最近交易信息等。一般而言在使用 Redis 存储的时候,需要从 3 个方面进行考虑。

    • 业务数据常用吗?命中率如何?如果命中率很低,就没有必要写入缓存。
    • 该业务数据是读操作多,还是写操作多,如果写操作多,频繁需要写入数据库,也没有必要使用缓存。
    • 业务数据大小如何?如果要存储几百兆字节的文件,会给缓存带来很大的压力,有没有必要?

    在考虑过这些问题后,如果觉得有必要使用缓存,那么就使用它。使用 Redis 作为缓存的读取逻辑如图 1 所示。
    在这里插入图片描述
    从图 1 中可以知道以下两点。
    当第一次读取数据的时候,读取 Redis 的数据就会失败,此时会触发程序读取数据库,把数据读取出来,并且写入 Redis。
    当第二次及以后读取数据时,就直接读取 Redis,读到数据后就结束了流程,这样速度就大大提高了。

    从上面的分析可知,大部分的操作是读操作,使用 Redis 应对读操作,速度就会十分迅速,同时也降低了对数据库的依赖,大大降低了数据库的负担。

    分析了读操作的逻辑后,下面再来分析写操作的流程,如图 2 所示。
    在这里插入图片描述
    从流程可以看出,更新或者写入的操作,需要多个 Redis 的操作。如果业务数据写次数远大于读次数没有必要使用 Redis。如果是读次数远大于写次数,则使用 Redis 就有其价值了,因为写入 Redis 虽然要消耗一定的代价,但是其性能良好,相对数据库而言,几乎可以忽略不计。

    高速读/写场合

    在互联网的应用中,往往存在一些需要高速读/写的场合,比如商品的秒杀,抢红包,淘宝、京东的双十一活动或者春运抢票等。

    以上这类场合在一个瞬间成千上万的请求就会达到服务器,如果使用的是数据库,一个瞬间数据库就需要执行成千上万的 SQL,很容易造成数据库的瓶颈,严重的会导致数据库瘫痪,造成 Java Web 系统服务崩溃。

    在这样的场合的应对办法往往是考虑异步写入数据库,而在高速读/写的场合中单单使用 Redis 去应对,把这些需要高速读/写的数据,缓存到 Redis 中,而在满足一定的条件下,触发这些缓存的数据写入数据库中。先看看一次请求操作的流程图,如图 3 所示。
    在这里插入图片描述
    进一步论述这个过程:

    当一个请求达到服务器,只是把业务数据先在 Redis 读/写,而没有进行任何对数据库的操作,换句话说系统仅仅是操作 Redis 缓存,而没有操作数据库,这个速度就比操作数据库要快得多,从而达到需要高速响应的效果。

    但是一般缓存不能持久化,或者所持久化的数据不太规范,因此需要把这些数据存入数据库,所以在一个请求操作完 Redis 的读/写后,会去判断该高速读/写的业务是否结束。

    这个判断的条件往往就是秒杀商品剩余个数为 0,抢红包金额为 0,如果不成立,则不会操作数据库;如果成立,则触发事件将 Redis 缓存的数据以批量的形式一次性写入数据库,从而完成持久化的工作。

    假设面对的是一个商品秒杀的场景,从上面的流程看,一个用户抢购商品,绝大部分的场合都是在操作内存数据库 Redis,而不是磁盘数据库,所以其性能更为优越。只有在商品被抢购一空后才会触发系统把 Redis 缓存的数据写入数据库磁盘中,这样系统大部分的操作基于内存,就能够在秒杀的场合高速响应用户的请求,达到快速应答。

    而现实中这种需要高速响应的系统会比上面的分析更复杂,因为这里没有讨论高并发下的数据安全和一致性问题,没有讨论有效请求和无效请求、事务一致性等诸多问题,这些将会在未来以独立教程讨论它。

    Redis基本安装和使用

    Window 下安装

    下载地址:https://github.com/tporadowski/redis/releases

    Redis 支持 32 位和 64 位。这个需要根据你系统平台的实际情况选择,这里我们下载 Redis-x64-xxx.zip压缩包到 C 盘,解压后,将文件夹重新命名为 redis。
    在这里插入图片描述

    打开一个 cmd 窗口 使用 cd 命令切换目录到 C:\redis 运行:

    redis-server.exe redis.windows.conf
    

    为了方便,我们在这个目录新建一个文件 startup.cmd,用记事本或者其他文本编辑工具打开(要是打不开,可以先写入内容在重命名),然后写入以下内容。

    redis-server redis.windows.conf
    

    这个命令调用 redis-server.exe 的命令读取 redis.window.conf 的内容,用来启动 redis,保存好了 startup.cmd 文件,双击它就可以看到 Redis 启动的信息了,会显示如下界面:
    在这里插入图片描述
    这时候另启一个 cmd 窗口,原来的不要关闭,不然就无法访问服务端了。切换到 redis 目录下运行:

    redis-cli.exe -h 127.0.0.1 -p 6379
    

    也可以双击放在同一个文件夹下的文件 redis-cli.exe,它是一个 Redis 自带的客户端工具,这样就可以连接到 Redis 服务器了,如图 所示。
    在这里插入图片描述

    Linux 下安装

    下载地址:http://redis.io/download,下载最新稳定版本。

    本教程使用的最新文档版本为 2.8.17,下载并安装:

    $ wget http://download.redis.io/releases/redis-2.8.17.tar.gz
    $ tar xzf redis-2.8.17.tar.gz
    $ cd redis-2.8.17
    $ make
    

    make完后 redis-2.8.17目录下会出现编译后的redis服务程序redis-server,还有用于测试的客户端程序redis-cli,两个程序位于安装目录 src 目录下:

    下面启动redis服务.

    $ cd src
    $ ./redis-server
    

    注意这种方式启动redis 使用的是默认配置。也可以通过启动参数告诉redis使用指定配置文件使用下面命令启动。

    $ cd src
    $ ./redis-server ../redis.conf
    

    redis.conf 是一个默认的配置文件。我们可以根据需要使用自己的配置文件。

    启动redis服务进程后,就可以使用测试客户端程序redis-cli和redis服务交互了。 比如:

    $ cd src
    $ ./redis-cli
    redis> set foo bar
    OK
    redis> get foo
    "bar"
    

    Ubuntu 下安装

    在 Ubuntu 系统安装 Redis 可以使用以下命令:

    $sudo apt-get update
    $sudo apt-get install redis-server
    

    启动 Redis

    $ redis-server
    

    查看 redis 是否启动?

    $ redis-cli
    

    以上命令将打开以下终端:

    redis 127.0.0.1:6379>
    

    127.0.0.1 是本机 IP ,6379 是 redis 服务端口。现在我们输入 PING 命令。

    redis 127.0.0.1:6379> ping
    PONG
    

    以上说明我们已经成功安装了redis。

    在Java程序中使用Redis

    在 Java 中使用 Redis 工具,要先下载 jedis.Jar 包,把它加载到工程的路径中

     <!-- redis包 -->
        <dependency>
          <groupId>redis.clients</groupId>
          <artifactId>jedis</artifactId>
          <version>2.9.0</version>
        </dependency>
    

    添加到maven项目就可以了,可以使用如下代码进行测试:

    package org.在Java中使用redis;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    /**
     * 测试redis每秒写入性能
     *
     */
    public class Test
    {
        public static void main( String[] args )
        {
            JedisPoolConfig poolConfig=new JedisPoolConfig();
            poolConfig.setMaxIdle(50);//最大空闲数;
            poolConfig.setMaxTotal(100);//最大连接数
            poolConfig.setMaxWaitMillis(200000);//最大等待毫秒数
            //使用连接池配置创建连接池
            JedisPool jedisPool=new JedisPool(poolConfig,"localhost");
            //从连接池获取单个连接
            Jedis jedis=jedisPool.getResource();
            //jedis.auth("password");//如果需要密码
    
            // Jedis jedis=new Jedis("localhost",6379);//连接Redis
            int i=0;
            long start=System.currentTimeMillis();
            while (true){
                long end=System.currentTimeMillis();
                if(end-start>=1000){
                    break;
                }
                i++;
                jedis.set("test"+i,i+"");
            }
            jedis.close();
            System.out.println("redis每秒操作"+i+"次");
        }
    }
    
    

    Java Redis 的连接池提供了类 redis.clients.jedis.JedisPool 用来创建 Redis 连接池对象。使用这个对象,需要使用类 redis.clients.jedis.JedisPoolConfig 对连接池进行配置。

    由于 Redis 只能提供基于字符串型的操作,而在 Java 中使用的却以类对象为主,所以需要 Redis 存储的字符串和 Java 对象相互转换。

    如果自己编写这些规则,工作量还是比较大的,比如一个角色对象,我们没有办法直接把对象存入 Redis 中,需要进一步进行转换,所以对操作对象而言,使用 Redis 还是比较难的。

    好在 Spring 对这些进行了封装和支持,它提供了序列化的设计框架和一些序列化的类,使用后它可以通过序列化把 Java 对象转换,使得 Redis 能把它存储起来。

    并且在读取的时候,再把由序列化过的字符串转化为 Java 对象,这样在 Java 环境中使用 Redis 就更加简单了,所以更多的时候可以使用 Spring 提供的 RedisTemplate 的机制来使用 Redis。

    在Spring中使用Redis

    在 Spring 中使用 Redis,除了需要 jedis.jar 外,还需要下载 spring-data-redis.jar,这里值得注意的是 jar 包和 Spring 版本兼容的问题。

    <!-- redis包 -->
        <dependency>
          <groupId>redis.clients</groupId>
          <artifactId>jedis</artifactId>
          <version>2.9.0</version>
        </dependency>
        <!-- 使用 Spring 提供的 RedisTemplate 操作 Redis -->
          <!--spring-data-redis跟jedis版本要对应-->
        <dependency>
          <groupId>org.springframework.data</groupId>
          <artifactId>spring-data-redis</artifactId>
          <version>1.6.2.RELEASE</version>
        </dependency>
    

    完整的pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>org.example</groupId>
      <artifactId>Redis_stu</artifactId>
      <version>1.0-SNAPSHOT</version>
    
      <name>Redis_stu</name>
      <!-- FIXME change it to the project's website -->
      <url>http://www.example.com</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
    
        <!-- redis包 -->
        <dependency>
          <groupId>redis.clients</groupId>
          <artifactId>jedis</artifactId>
          <version>2.9.0</version>
        </dependency>
        <!-- 使用 Spring 提供的 RedisTemplate 操作 Redis -->
          <!--spring-data-redis跟jedis版本要对应-->
        <dependency>
          <groupId>org.springframework.data</groupId>
          <artifactId>spring-data-redis</artifactId>
          <version>1.6.2.RELEASE</version>
        </dependency>
    
        <!--4:spring依赖-->
        <!--1)spring核心依赖-->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
          <version>4.1.7.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-beans</artifactId>
          <version>4.1.7.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.1.7.RELEASE</version>
        </dependency>
        <!--2)spring dao层依赖-->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>4.1.7.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
          <version>4.1.7.RELEASE</version>
        </dependency>
        <!--3)springweb相关依赖-->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
          <version>4.1.7.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>4.1.7.RELEASE</version>
        </dependency>
        <!--4)spring test相关依赖-->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>4.1.7.RELEASE</version>
        </dependency>
      </dependencies>
    
      <build>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
          <plugins>
            <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
            <plugin>
              <artifactId>maven-clean-plugin</artifactId>
              <version>3.1.0</version>
            </plugin>
            <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
            <plugin>
              <artifactId>maven-resources-plugin</artifactId>
              <version>3.0.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-compiler-plugin</artifactId>
              <version>3.8.0</version>
            </plugin>
            <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <version>2.22.1</version>
            </plugin>
            <plugin>
              <artifactId>maven-jar-plugin</artifactId>
              <version>3.0.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-install-plugin</artifactId>
              <version>2.5.2</version>
            </plugin>
            <plugin>
              <artifactId>maven-deploy-plugin</artifactId>
              <version>2.8.2</version>
            </plugin>
            <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
            <plugin>
              <artifactId>maven-site-plugin</artifactId>
              <version>3.7.1</version>
            </plugin>
            <plugin>
              <artifactId>maven-project-info-reports-plugin</artifactId>
              <version>3.0.0</version>
            </plugin>
          </plugins>
        </pluginManagement>
      </build>
    </project>
    
    

    在大部分情况下我们都会用到连接池,于是先用 Spring 配置一个 JedisPoolConfig 对象,这个配置相对而言比较简单,使用 Spring 配置 JedisPoolConfig 对象代码如下所示。

    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大空闲数 -->
        <property name="maxIdle" value="50" />
        <!-- 最大连接数 -->
        <property name="maxTotal" value="100" />
        <!-- 最大等待时间 -->
        <property name="maxWaitMillis" value="20000" />
    </bean>
    

    这样就设置了一个连接池的配置,继续往下配置。

    在使用 Spring 提供的 RedisTemplate 之前需要配置 Spring 所提供的连接工厂,在 Spring Data Redis 方案中它提供了 4 种工厂模型。

    • JredisConnectionFactory。
    • JedisConnectionFactory。
    • LettuceConnectionFactory。
    • SrpConnectionFactory。

    虽然使用哪种实现工厂都是可以的,但是要根据环境进行测试,以验证使用哪个方案的性能是最佳的。无论如何它们都是接口 RedisConnectionFactory 的实现类,更多的时候我们都是通过接口定义去理解它们,所以它们是具有接口适用性特性的。本教程将以使用最为广泛的 JedisConnectionFactory 为例进行讲解。

    例如,在 Spring 中配置一个 JedisConnectionFactory 对象,配置 JedisConnectionFactory 代码如下所示。

    <bean id="connectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="localhost" />
        <property name="port" value="6379" />
        <!--<property name="password" value="password"/> -->
        <property name="poolConfig" ref="poolConfig" />
    </bean>
    

    解释一下它的属性配置。

    • hostName,代表的是服务器,默认值是 localhost,所以如果是本机可以不配置它。
    • port,代表的是接口端口,默认值是 6379,所以可以使用默认的 Redis 端口,也可以不配置它。
    • password,代表的是密码,在需要密码连接 Redis 的场合需要配置它。
    • poolConfig,是连接池配置对象,可以设置连接池的属性。

    这样就完成了一个 Redis 连接工厂的配置。这里配置的是 JedisConnectionFactory,如果需要的是 LettuceConnectionFactory,可以把使用 Spring 配置 JedisPoolConfig 对象代码中的 Bean 元素的 class 属性修改为 org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor 即可,这取决于项目的需要和特殊性。有了 RedisConnectionFactory 工厂,就可以使用 RedisTemplate 了。

    普通的连接使用没有办法把 Java 对象直接存入 Redis,而需要我们自己提供方案,这时往往就是将对象序列化,然后使用 Redis 进行存储,而取回序列化的内容后,在通过转换转变为 Java 对象,Spring 模板中提供了封装的方案,在它内部提供了 RedisSerializer 接口(org.springframework.data.redis.serializer.RedisSerializer)和一些实现类,其原理如图 1 所示。
    在这里插入图片描述
    可以选择 Spring 提供的方案去处理序列化,当然也可以去实现在 spring data redis 中定义的 RedisSerializer 接口,在 Spring 中提供了以下几种实现 RedisSerializer 接口的序列化器。

    • GenericJackson2JsonRedisSerializer,通用的使用 Json2.jar 的包,将 Redis 对象的序列化器。
    • Jackson2JsonRedisSerializer,通过 Jackson2.jar 包提供的序列化进行转换(由于版本太旧,Spring 不推荐使用)。
    • JdkSerializationRedisSerializer,使用 JDK 的序列化器进行转化。
    • OxmSerializer,使用 Spring O/X 对象 Object 和 XML 相互转换。
    • StringRedisSerializer,使用字符串进行序列化。
    • GenericToStringSerializer,通过通用的字符串序列化进行相互转换。

    使用它们就能够帮助我们把对象通过序列化存储到 Redis 中,也可以把 Redis 存储的内容转换为 Java 对象,为此 Spring 提供的 RedisTemplate 还有两个属性。

    • keySerializer——键序列器。
    • valueSerializer——值序列器。

    有了上面的了解,就可以配置 RedisTemplate 了。假设选用 StringRedisSerializer 作为 Redis 的 key 的序列化器,而使用 JdkSerializationRedisSerializer 作为其 value 的序列化器,则可以以下代码的方法来配置 RedisTemplate。

    spring-config.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
            <!--用 Spring 配置一个 JedisPoolConfig 对象-->
            <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
                <property name="maxIdle" value="50"/><!--最大空闲-->
                <property name="maxTotal" value="100"/><!--最大连接-->
                <property name="maxWaitMillis" value="20000"/>
            </bean>
        <!--使用 Spring 提供的 RedisTemplate 之前需要配置 Spring 所提供的连接工厂-->
        <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
            <property name="hostName" value="localhost"/>
            <property name="port" value="6379"/>
            <property name="poolConfig" ref="jedisPoolConfig"/>
        </bean>
        <!--选用 StringRedisSerializer 作为 Redis 的 key 的序列化器,
        而使用 JdkSerializationRedisSerializer 作为其 value 的序列化器,则可以以下代码的方法来配置 RedisTemplate-->
        <bean id="jdkSerializationRedisSerializer"
              class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
        <bean id="stringRedisSerializer"
              class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
            <property name="connectionFactory" ref="connectionFactory"/>
            <property name="keySerializer" ref="stringRedisSerializer"/>
            <property name="valueSerializer" ref="jdkSerializationRedisSerializer"/>
        </bean>
    </beans>
    

    这样就配置了一个 RedisTemplate 的对象,并且 spring data redis 知道会用对应的序列化器去转换 Redis 的键值。

    举个例子,新建一个角色对象,使用 Redis 保存它的对象,使用 Redis 保存角色类对象如下所示。

    package spring提供redisTemplate使用redis;
    
    import java.io.Serializable;
    
    public class Role implements Serializable {
        /**
         * 注意,对象要可序列化,需要实现Serializable接口,往往要重写serialVersionUID
         */
        public static final long serialVersionUID=1L;
        private long id;
        private String roleName;
        private String note;
    
        @Override
        public String toString() {
            return "Role{" +
                    "id=" + id +
                    ", roleName='" + roleName + '\'' +
                    ", note='" + note + '\'' +
                    '}';
        }
    
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
        public String getRoleName() {
            return roleName;
        }
    
        public void setRoleName(String roleName) {
            this.roleName = roleName;
        }
    
        public String getNote() {
            return note;
        }
    
        public void setNote(String note) {
            this.note = note;
        }
    }
    
    

    因为要序列化对象,所以需要实现 Serializable 接口,表明它能够序列化,而 serialVersionUID 代表的是序列化的版本编号。

    接下来就可以测试保存这个 Role 对象了,测试代码如下所示。

    package spring提供redisTemplate使用redis;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.data.redis.core.RedisTemplate;
    
    public class Test {
        public static void main(String[] args) {
            ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-config.xml");
            RedisTemplate redisTemplate = (RedisTemplate) applicationContext.getBean("redisTemplate");
            Role role=new Role();
            role.setId(1L);
            role.setRoleName("name_1");
            role.setNote("note_1");
            redisTemplate.opsForValue().set("role_1", role);
            Role role_1 = (Role) redisTemplate.opsForValue().get("role_1");
            System.out.println(role_1.toString());
    
        }
    }
    
    

    测试结果
    在这里插入图片描述
    显然这里已经成功保存和获取了一个 Java 对象,这段代码演示的是如何使用 StringRedisSerializer 序列化 Redis 的 key,而使用 JdkSerializationRedisSerializer 序列化 Redis 的value,当然也可以根据需要去选择,甚至是自定义序列化器。

    SessionCallback

    注意,以上的使用都是基于 RedisTemplate、基于连接池的操作,换句话说,并不能保证每次使用 RedisTemplate 是操作同一个对 Redis 的连接,比如上面代码中的下面两行代码。set 和 get 方法看起来很简单,它可能就来自于同一个 Redis 连接池的不同 Redis 的连接。

    为了使得所有的操作都来自于同一个连接,可以使用 SessionCallback 或者 RedisCallback 这两个接口,而 RedisCallback 是比较底层的封装,其使用不是很友好,所以更多的时候会使用 SessionCallback 这个接口,通过这个接口就可以把多个命令放入到同一个 Redis 连接中去执行,代码如下所示,它主要是实现了上面代码中的功能。

    public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
            RedisTemplate<String,Role> redisTemplate= applicationContext.getBean(RedisTemplate.class);
            final Role role=new Role();
            role.setId(1L);
            role.setRoleName("name_2");
            role.setNote("note_2");
            SessionCallback<Role> sessionCallback = new SessionCallback<Role>() {
                @Override
                public Role execute(RedisOperations redisOperations) throws DataAccessException {
                    redisOperations.boundValueOps("role_2").set(role);
                    return (Role) redisOperations.boundValueOps("role_2").get();
                }
            };
            Role savedRole = redisTemplate.execute(sessionCallback);
            System.out.println(savedRole.toString());
        }
    

    这样 set 和 get 命令就能够保证在同一个连接池的同一个 Redis 连接进行操作,这里向读者展示的是使用匿名类的形式,而事实上如果采用 Java 8 的 JDK 版本,也可以使用 Lambda 表达式进行编写 SessionCallback 的业务逻辑,这样逻辑会更为清晰明了。由于前后使用的都是同一个连接,因此对于资源损耗就比较小,在使用 Redis 操作多个命令或者使用事务时也会常常用到它。

    Redis的6种数据类型

    NoSQL和传统数据库有什么区别?NoSQL能取代传统数据库吗?

    Redis字符串数据结构和常用命令

    Redis哈希数据结构和常用命令

    Redis链表(linked-list)数据结构和常用命令

    Redis集合数据结构和常用命令

    Redis有序集合(sorted set)串数据结构和常用命令

    Redis HyperLogLog常用命令

    Redis的基础事务和常用操作

    探索Redis事务回滚

    Redis watch命令——监控事务

    使用流水线(pipelined)提高Redis的命令性能

    Redis发布订阅模式

    Redis的超时命令和垃圾回收策略

    Redis中使用Lua语言

    Redis的两种备份(持久化)方式:RDB和AOF

    Redis内存回收策略

    Redis主从复制的配置方法和执行过程

    Redis哨兵(Sentinel)模式的配置方法及其在Java中的用法

    Redis和数据库的结合

    Spring整合Redis详细步骤

    从RedisTemplate中获得Jedis实例

    互联网系统应用架构基础分析

    在互联网系统中包含许多的工具,每个企业都有自己的架构,正如没有完美的程序一样,也不会有完美的架构。本节分析的架构严格来说并不严谨,但是却包含了互联网的思想,互联网架构如图 1 所示。
    在这里插入图片描述
    这不是一个严谨的架构,但是它包含了互联网的许多特性。对于防火墙,无非是防止互联网上的病毒和其他攻击,正常的请求通过防火墙后,最先到达的就是负载均衡器,这是关注的核心。

    负载均衡器有以下几个功能:

    1)对业务请求做初步的分析,决定分不分发请求到 Web 服务器,这就好比一个把控的关卡,常见的分发软件比如 Nginx 和 Apache 等反向代理服务器,它们在关卡处可以通过配置禁止一些无效的请求。

    比如封禁经常作弊的 IP 地址,也可以使用 Lua、C 语言联合 NoSQL 缓存技术进行业务分析,这样就可以初步分析业务,决定是否需要分发到服务器。

    2)提供路由算法,它可以提供一些负载均衡的算法,根据各个服务器的负载能力进行合理分发,每一个 Web 服务器得到比较均衡的请求,从而降低单个服务器的压力,提高系统的响应能力。

    3)限流,对于一些高并发时刻,如双十一、新产品上线,需要通过限流来处理,因为可能某个时刻通过上述的算法让有效请求过多到达服务器,使得一些 Web 服务器或者数据库服务器产生宕机。

    当某台机器宕机后,会使得其他服务器承受更大的请求量,这样就容易产生多台服务器连续宕机的可能性,持续下去就会引发服务器雪崩。

    因此在这种情况下,负载均衡器有限流的算法,对于请求过多的时刻,可以告知用户系统繁忙,稍后再试,从而保证系统持续可用。

    如果顺利通过了防火墙和负载均衡器的请求,那么负载均衡器就会通过设置的算法进行计算后,将请求分发到某一台 Web 服务器上,由 Web 服务器通过分布式的 NoSQL 和数据库提供服务,这样就能够高效响应客户端的请求了。

    从上面的分析可以知道,系统完全可以在负载均衡器中进行初步鉴别业务请求,使得一些不合理的业务请求在进入 Web 服务器之前就被排除掉,而为了应对复杂的业务,可以把业务存储在 NoSQL(往往是 Redis)上,通过 C 语言或者 Lua 语言进行逻辑判断,它们的性能比 Web 服务器判断的性能要快速得多。

    通过这些简单的判断就能够快速发现无效请求,并把它们排除在 Web 服务器之外,从而降低 Web 服务器的压力,提高互联网系统的响应速度,不过在进一步分析之前,我们还要鉴别无效请求,教程后面会讨论有效请求和无效请求。

    高并发系统的分析和设计

    任何系统都不是独立于业务进行开发的,真正的系统是为了实现业务而开发的,所以开发高并发网站抢购时,都应该先分析业务需求和实际的场景,在完善这些需求之后才能进入系统开发阶段。

    没有对业务进行分析就贸然开发系统是开发者的大忌。对于业务分析,首先是有效请求和无效请求,有效请求是指真实的需求,而无效请求则是虚假的抢购请求。

    有效请求和无效请求

    无效请求有很多种类,比如通过脚本连续刷新网站首页,使得网站频繁访问数据库和其他资源,造成性能持续下降,还有一些为了得到抢购商品,使用刷票软件连续请求的行为。

    鉴别有效请求和无效请求是获取有效请求的高并发网站业务分析的第一步,我们现在来分析哪些是无效请求的场景,以及应对方法。

    首先,一个账号连续请求,对于一些懂技术或者使用作弊软件的用户,可以使用软件对请求的服务接口连续请求,使得后台压力变大,甚至在一秒内发送成百上千个请求到服务器。

    这样的请求显然可以认为是无效请求,应对它的方法很多,常见的做法是加入验证码。一般而言,首次无验证码以便用户减少录入,第二次请求开始加入验证码,可以是图片验证码、等式运算等。

    使用图片验证码可能存在识别图片作弊软件的攻击,所以在一些互联网网站中,图片验证码还会被加工成为东倒西歪的形式,这样增加了图片识别作弊软件的辨别难度,以压制作弊软件的使用。简单的等式运算,也会使图片识别作弊软件更加难以辨认。

    其次,使用短信服务,把验证码发送到短信平台以规避部分作弊软件。

    在企业应用中,这类问题的逻辑判断,不应该放在Web服务器中实现,而应放在负载均衡器上完成,即在进入 Web 服务器之前完成,做完这一步就能避免大量的无效请求,对保证高并发服务器可用性很有效果。

    仅仅做这一步或许还不够,毕竟验证码或许还有其他作弊软件可以快速读取图片或者短信信息,从而发送大量的请求。进一步的限制请求,比如限制用户在单位时间的购买次数以压制其请求量,使得这些请求排除在服务器之外。判断验证码逻辑,如图 1 所示。
    在这里插入图片描述
    这里的判断是在负载均衡转发给 Web 服务器前,对验证码和单位时间单个账号请求数量进行判断。这里使用了 C 语言和 Redis 进行判断,那么显然这套方案会比 Java 语言和数据库机制的性能要高得多,通过这套体系,基本能够压制一个用户对系统的作弊,也提高了整个系统验证的性能。

    这是对一个账号连续无效请求的压制,有时候有些用户可能申请多个账号来迷惑服务器,使得他可以避开对单个账户的验证,从而获得更多的服务器资源。

    一个人多个账户的场景还是比较好应付的,可以通过提高账户的等级来压制多个请求,比如对于支付交易的网站,可以通过银行卡验证,实名制获取相关证件号码,从而使用证件号码使得多个账户归结为一人,通过这层关系来屏蔽多个账号的频繁请求,这样就有效地规避了一个人多个账号的频繁请求。

    对于有组织的请求,则不是那么容易了,因为对于一些黄牛组织,可能通过多人的账号来发送请求,统一组织伪造有效请求,如图 2 所示。
    在这里插入图片描述
    对于这样的请求,我们会考虑使用僵尸账号排除法对可交易的账号进行排除,所谓僵尸账号,是指那些平时没有任何交易的账号,只是在特殊的日子交易,比如春运期间进行大批量抢购的账号。

    当请求达到服务器,我们通过僵尸账号,排除掉一些无效请求。当然还能使用 IP 封禁,尤其是通过同一 IP 或者网段频繁请求的,但是这样也许会误伤有效请求,所以使用 IP 封禁还是要慎重一些。

    系统设计

    高并发系统往往需要分布式的系统分摊请求的压力,这就需要使用负载均衡服务了,它进行简易判断后就会分发到具体 Web 服务器。

    我们要尽量根据 Web 服务器的性能进行均衡分配请求,使得单个 Web 服务器压力不至于过大,导致服务瘫痪,这可以参考 Nginx 的请求分发,这样使得请求能够均衡发布到服务器中去,服务器可以按业务划分。

    比如当前的购买分为产品维护、交易维护、资金维护、报表统计和用户维护等模块,按照功能模块进行区分,使得它们相互隔离,就可以降低数据的复杂性,图 3 就是一种典型的按业务划分,或者称为水平分法。
    在这里插入图片描述
    按照业务划分的好处是:首先,一个服务管理一种业务,业务简单了,提高了开发效率,其次,数据库的设计也方便许多,毕竟各管各的东西。

    但是,这也会带来很多麻烦,比如由于各个系统业务存在着关联,还要通过 RPC(Remote Procedure Call Protoco,远程过程调用协议)处理这些关联信息。

    比较流行的 RPC 有 Dubbo、Thrift 和 Hessian 等。其原理是,每一个服务都会暴露一些公共的接口给 RPC 服务,这样对于任何一个服务器都能够通过 RPC 服务获取其他服务器对应的接口去调度各个服务器的逻辑来完成功能,但是接口的相互调用也会造成一定的缓慢。

    有了水平分法也会有垂直分法,所谓垂直分法就是将一个很大的请求量,不按子系统分,而是将它们按照互不相干的几个同样的系统分摊下去。

    比如一台服务器的最大负荷为每秒 1 万个请求,而测得系统高峰为每秒 2 万个请求,如果我们把各个请求按照一定的算法合理分配到 4 台服务器上,那么 4 台服务器平均 5 千个请求就属于正常服务了,这样的路由算法被称为垂直分法,如图 4 所示。
    在这里插入图片描述
    垂直分法不按业务分,对于负载均衡器的算法往往可以通过用户编号把路由分发到对应的服务器上。

    每一个服务器处理自己独立的业务,互不干扰,但是每一个服务器都包含所有的业务逻辑功能,会造成开发上的业务困难,对于数据库设计而言也是如此。

    对于大型网站还会有更细的分法,比如水平和垂直结合的分法,如图 5 所示。
    在这里插入图片描述
    首先将系统按照业务区分为多个子系统,然后在每一个子系统下再分多个服务器,通过每一个子系统的路由器找到对应的子系统服务器提供服务。

    分法是多样性的,每一个企业都会根据自己的需要而进行不同的设计,但是无论系统如何分,秉承的原则是不变的。

    首先,服务器的负载均衡,要使得每一个服务器都能比较平均地得到请求数量,从而提高系统的吞吐和性能。其次,业务简化,按照模块划分可以使得系统划分为各个子系统,这样开发者的业务单一化,就更容易理解和开发了。

    数据库设计

    对于数据库的设计而言,为了得到高性能,可以使用分表或分库技术,从而提高系统的响应能力。

    分表是指在一个数据库内本来一张表可以保存的数据,设计成多张表去保存,比如交易表 t_transaction。

    由于存储数据多会造成查询和统计的缓慢,这个时候可以使用多个表存储,比如 2016 年的数据用表 t_transaction_2016 存储,2017 年的数据使用表 t_transaction_2017 存储,2018 年的数据则用表 t_transaction_2018 存储,依此类推,开发者只要根据查询的年份确定需要查找哪张表就可以了,如图 6 所示。
    在这里插入图片描述
    分库则不一样,它把表数据分配在不同的数据库中,比如上述的交易表 t_transaction 可以存放在多个数据库中,如图 7 所示。
    在这里插入图片描述
    分库数据库首先需要一个路由算法确定数据在哪个数据库上,然后才能进行查询,比如我们可以把用户和对应业务的数据库的信息缓存到 Redis 中,这样路由算法就可以通过 Redis 读取的数据来决定使用哪个数据库进行查询了。

    一些会员很多的网站还可以区分活跃会员和非活跃会员。活跃会员可以通过数据迁徙的手段,也就是先记录在某个时间段(比如一个月的月底)会员的活跃度,然后通过数据迁徙,将活跃会员较平均分摊到各个数据库中,以避免某个库过多的集中活跃会员,而导致个别数据库被访问过多,从而达到数据库的负载均衡。

    做完这些还可以考虑优化 SQL,建立索引等优化,提高数据库的性能。性能低下的 SQL 对于高并发网站的影响是很大的,这些对开发者提出了更高的要求。

    在开发网站中使用更新语句和复杂查询语句要时刻记住更新是表锁定还是行锁定,比如 id 是主键,而 user_name 是用户名称,也是唯一索引,更新用户的生日,可以使用以下两条SQL中的任何一条:
    update t_user set birthday = #{birthday} where id = #{id};
    update t_user set birthday = #{birthday} where user_name = #{userName};

    上述逻辑都是正确的,但是优选使用主键更新,其原因是在 MySQL 的运行过程中,第二句 SQL 会锁表,即不仅锁定更新的数据,而且锁定其他表的数据,从而影响并发,而使用主键的更新则是行锁定。
    对于 SQL 的优化还有很多细节,比如可以使用连接查询代替子查询。查询一个没有分配角色的用户 id,可能有人使用这样的一个 SQL:
    select u.id from t_user u
    where u.id not in (select ur.user_id from t_user_role ur);

    这是一个 not in 语句,性能低下,对于这样的 not in 和 not exists 语句,应该全部修改为连接语句去执行,从而极大地提高 SQL 的性能,比如这条 not in 语句可以修改为:

    select u.id from t_user u left join t_user_role ur
    on u.id = ur.user_id
    where ur.user_id is null;
    

    not in 语句消失了,使用了连接查询,大大提高了 SQL 的执行性能。

    此外还可以通过读/写分离等技术,进行进一步的优化,这样就可以有一台主机主要负责写业务,一台或者多台备机负责读业务,有助于性能的提高。

    对于分布式数据库而言,还会有另外一个麻烦,就是事务的一致性,事务的一致性比较复杂,目前流行的有两段提交协议,即 XA 协议、Paxos 协议。

    动静分离技术

    动静分离技术是目前互联网的主流技术,对于互联网而言大部分数据都是静态数据,只有少数使用动态数据,动态数据的数据包很小,不会造成网络瓶颈。

    而静态的数据则不一样,静态数据包含图片、CSS(样式)、JavaScript(脚本)和视频等互联网的应用,尤其是图片和视频占据的流量很大,如果都从动态服务器(比如 Tomcat、WildFly 和 WebLogic 等)获取,那么动态服务器的带宽压力会很大,这个时候应该考虑使用动静分离技术。

    对于一些有条件的企业也可以考虑使用 CDN(Content Delivery Network,即内容分发网络)技术,它允许企业将自己的静态数据缓存到网络 CDN 的节点中。比如企业将数据缓存在北京的节点上,当在天津的客户发送请求时,通过一定的算法,会找到北京 CDN 节点,从而把 CDN 缓存的数据发送给天津的客户,完成请求。

    对于深圳的客户,如果企业将数据缓存到广州 CDN 节点上,那么它也可以从广州的 CDN 节点上取出数据,由于就近取出缓存节点的数据,所以速度会很快,如图 8 所示。
    在这里插入图片描述
    一些企业也许需要自己的静态 HTTP 服务器,将静态数据分离到静态 HTTP 服务器上。其原理大同小异,就是将资源分配到静态服务器上,这样图片、HTML、脚本等资源都可以从静态服务器上获取,尽量使用 Cookie 等技术,让客户端缓存能够缓存数据,避免多次请求,降低服务器的压力。

    对于动态数据,则需要根据会员登录来获取后台数据,这样的动态数据是高并发网站关注的重点。

    锁和高并发

    无论区分有效请求和无效请求,水平划分和垂直划分,动静分离技术,还是数据库分表、分库等技术的应用,都无法避免动态数据,而动态数据的请求最终也会落在一台 Web 服务器上。

    对于一台 Web 服务器而言,如果是 Java 服务器,它极有可能采用 SSM 框架结合数据库和 Redis 等技术提供服务,那么它会面临何种困难呢?高并发系统存在的一个麻烦是并发数据不一致问题。

    以抢红包为例,发放了一个总额为 20 万元的红包,它可以拆分为 2 万个可抢的小红包。假设每个小红包都是 10 元,供给网站会员抢夺,网站同时存在 3 万会员在线抢夺,这就是一个典型的高并发的场景。

    以上会出现多个线程同时享有大红包数据的场景,在高并发的场景中,由于线程每一步完成的顺序不一样,这样会导致数据的一致性问题,比如在最后的一个红包,就可能出现如表 1 所示的场景。

    注意表 1 中加粗的文字,由此可见,在高并发的场景下可能出现错扣红包的情况,这样就会导致数据错误。由于在一个瞬间产生很高的并发,因此除了保证数据一致性,我们还要尽可能地保证系统的性能,加锁会影响并发,而不加锁就难以保证数据的一致性,这就是高并发和锁的矛盾。
    在这里插入图片描述
    为了解决这对矛盾,在当前互联网系统中,大部分企业提出了悲观锁和乐观锁的概念,而对于数据库而言,如果在那么短的时间内需要执行大量 SQL,对于服务器的压力可想而知,需要优化数据库的表设计、索引、SQL 语句等。

    有些企业提出了使用 Redis 事务和 Lua 语言所提供的原子性来取代现有的数据库的技术,从而提高数据的存储响应,以应对高并发场景,严格来说它也属于乐观锁的概念。教程后面会讨论关于数据不一致的方案,悲观锁、乐观锁和 Redis 实现的场景。

    使用Redis和SSM(Spring+Spring MVC+MyBatis)搭建抢红包开发环境和超发

    Redis悲观锁解决高并发抢红包的问题

    Redis乐观锁解决高并发抢红包的问题

    使用Redis和Lua的原子性实现抢红包功能

    Redis悲观锁、乐观锁和调用Lua脚本三种方式的优缺点

    内容出处

    http://c.biancheng.net/

    展开全文
  • Redis 数据库

    千次阅读 2018-10-16 16:16:12
    能够描述出什么是 nosql 能够说出 Redis 的特点 能够根据参考资料修改常用Redis配置 能够写出Redis中string类型数据的增删改查操作命令 能够写出Redis中hash类型数据的增删改查相关命令 能够说出Redis中 list ...

    1 学习目标

    1. 能够描述出什么是 nosql
    2. 能够说出 Redis 的特点
    3. 能够根据参考资料修改常用Redis配置
    4. 能够写出Redis中string类型数据的增删改查操作命令
    5. 能够写出Redis中hash类型数据的增删改查相关命令
    6. 能够说出Redis中 list 保存的数据类型
    7. 能够使用StrictRedis对象对string类型数据进行增删改查
    8. 能够参考步骤搭建 Redis 集群

    2 nosql介绍

    2.1 NoSQL:一类新出现的数据库(not only sql)

    • 泛指非关系型的数据库
    • 不支持SQL语法
    • 存储结构跟传统关系型数据库中的那种关系表完全不同,nosql中存储的数据都是KV形式
    • NoSQL的世界中没有一种通用的语言,每种nosql数据库都有自己的api和语法,以及擅长的业务场景
    • NoSQL中的产品种类相当多:
      • Mongodb
      • Redis
      • Hbase hadoop
      • Cassandra hadoop

    2.2 NoSQL和SQL数据库的比较

    • 适用场景不同:sql数据库适合用于关系特别复杂的数据查询场景,nosql反之
    • “事务”特性的支持:sql对事务的支持非常完善,而nosql基本不支持事务
    • 两者在不断地取长补短,呈现融合趋势

    3 Redis简介

    • Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。
    • Redis是 NoSQL技术阵营中的一员,它通过多种键值数据类型来适应不同场景下的存储需求,借助一些高层级的接口使用其可以胜任,如缓存、队列系统的不同角色
    • redis:一个key-value的高性能内存数据库,常用于session数据的保存

    3.1 Redis特性

    Redis 与其他 key - value 缓存产品有以下三个特点:

    • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
    • Redis不仅仅支持简单的key-value类型的数据,同时还提供string,list,hash,set,zset等数据结构的存储。
    • Redis支持数据的备份,即master-slave模式的数据备份。

    3.2 Redis 优势

    • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
    • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
    • 原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
    • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

    3.3 Redis应用场景

    • 用来做缓存(ehcache/memcached)——redis的所有数据是放在内存中的(内存数据库)
    • 可以在某些特定应用场景下替代传统数据库——比如社交类的应用
    • 在一些大型系统中,巧妙地实现一些特定的功能:session共享、购物车
    • 只要你有丰富的想象力,redis可以用在可以给你无限的惊喜…….

    3.4 推荐阅读

    4 Redis 安装

    VMware 上安装 Redis:

    • step1:下载

      wget http://download.redis.io/releases/redis-4.0.9.tar.gz

    • step2:解压

      tar xzf redis-4.0.9.tar.gz

    • step3:移动,放到usr/local目录下

      sudo mv ./redis-4.0.9 /usr/local/redis/

    • step4:进入redis目录

      cd /usr/local/redis/

    • step5:生成

      sudo make

    注意执行sudo make之前,确认下 /usr/local/redis目录下的文件是否是下图所示:

    只有有上述的红框文件,才能执行make成功

    • step6:测试,这段运行时间会较长

      sudo make test

    • step7:安装,将redis的命令安装到/usr/local/bin/目录

      sudo make install

    • step8:安装完成后,我们进入目录/usr/local/bin中查看

      cd /usr/local/bin
      ls -all

      • redis-server redis服务器
      • redis-cli redis命令行客户端
      • redis-benchmark redis性能测试工具
      • redis-check-aof AOF文件修复工具
      • redis-check-rdb RDB文件检索工具
    • step9:配置文件,移动到/etc/目录下

    • 配置文件目录为/usr/local/redis/redis.conf

      sudo cp /usr/local/redis/redis.conf /etc/redis/

    Mac 上安装 Redis:

    • 安装 Homebrew:

    https://brew.sh/

    • 使用 brew 安装 Redis 

    https://www.cnblogs.com/cloudshadow/p/mac_brew_install_redis.html

    5 配置

    • Redis的配置信息在/etc/redis/redis.conf下。

    • 查看

      sudo vim /etc/redis/redis.conf

    5.1 核心配置选项

    • 绑定ip:如果需要远程访问,可将此行注释,或绑定一个真实ip

      bind 127.0.0.1

    • 端口,默认为6379

      port 6379

    • 是否以守护进程运行

      • 如果以守护进程运行,则不会在命令行阻塞,类似于服务
      • 如果以不守护进程运行,则当前终端被阻塞
      • 设置为yes表示守护进程,设置为no表示不守护进程
      • 推荐设置为yes

      daemonize yes

    • 数据文件

      dbfilename dump.rdb

    • 数据文件存储路径

      dir /var/lib/redis

      千万注意:这个redis文件夹默认是没有的,需要自己创建

    • 日志文件

      logfile "/var/log/redis/redis-server.log"

      千万注意:这个redis文件夹默认是没有的,需要自己创建

    • 数据库,默认有16个

      database 16

      redis 默认给咱们创建了16个数据库,数据库的编号(名字)0-15

    • 主从复制,类似于双机备份。(目前不用理会)

      slaveof

    5.2 参考资料

    redis配置信息http://blog.csdn.net/ljphilp/article/details/52934933

    6 服务端和客户端命令

    6.1 服务端

    • 服务器端的命令为redis-server

    • 可以使用help查看帮助⽂档

      redis-server --help

    • 个人习惯

      ps aux | grep redis 查看redis服务器进程
      sudo kill -9 pid 杀死redis服务器
      sudo redis-server /etc/redis/redis.conf 指定加载的配置文件

    6.2 客户端

    • 客户端的命令为redis-cli
    • 可以使用help查看帮助文档

      redis-cli --help

    • 连接redis

      redis-cli

    • 运行测试命令

      ping

    • 切换数据库

    • 数据库没有名称,默认有16个,通过0-15来标识,连接redis默认选择第一个数据库

      select 10

         

    7 数据操作

    7.1 数据结构

    • redis是key-value的数据结构,每条数据都是一个键值对
    • 键的类型是字符串
    • 注意:键不能重复

    值的类型分为五种:

    • 字符串string
    • 哈希hash
    • 列表list
    • 集合set
    • 有序集合zset

    数据操作行为:

    • 保存
    • 修改
    • 获取
    • 删除

    点击中文官网查看命令文档http://redis.cn/commands.html

    7.2 字符串 string

    • 字符串类型是 Redis 中最为基础的数据存储类型,它在 Redis 中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等。在Redis中字符串类型的Value最多可以容纳的数据长度是512M。

    7.2.1 保存

    如果设置的键不存在则为添加,如果设置的键已经存在则修改

    • 设置键值

      set key value

    • 例1:设置键为name值为itcast的数据

      set name apollo

    • 设置键值及过期时间,以秒为单位

      setex key seconds value

    • 例2:设置键为aa值为aa过期时间为3秒的数据

      setex aa 3 aa

    • 设置多个键值

      mset key1 value1 key2 value2 ...

    • 例3:设置键为'a1'值为'python'、键为'a2'值为'java'、键为'a3'值为'c'

      mset a1 python a2 java a3 c

    • 追加值

      append key value

    • 例4:向键为a1中追加值' haha'

      append 'a1' 'haha'

    7.2.2 获取

    • 获取:根据键获取值,如果不存在此键则返回nil(不会报错)

      get key

    • 例5:获取键'name'的值

      get 'name'

    • 根据多个键获取多个值

      mget key1 key2 ...

    • 例6:获取键a1、a2、a3'的值

      mget a1 a2 a3

    7.2.3 删除

    详见下节键的操作,删除键时会将值删除

    7.3 键命令

    • 查找键,参数支持正则表达式

      keys pattern

    • 例1:查看所有键

      keys *

    • 例2:查看名称中包含a的键

      keys a*

    • 判断键是否存在,如果存在返回1,不存在返回0

      exists key

    • 例3:判断键a1是否存在

      exists aa

    • 查看键对应的value的类型

      type key

    • 例4:查看键a1的值类型,为redis⽀持的五种类型中的⼀种

      type aa

    • 删除键及对应的值

      del key1 key2 ...

    • 例5:删除键aa、a1

      del aa a1

    • 设置过期时间,以秒为单位如果没有指定过期时间则一直存在,直到使用DEL移除

    expire key seconds

    • 例6:设置键'a1'的过期时间为3秒

      expire 'a1' 3

    • 查看有效时间,以秒为单位

      ttl key

    • 例7:查看键'bb'的有效时间

      ttl bb

    7.4 hash类型

    • hash⽤于存储对象,对象的结构为属性、值
    • 值的类型为string

    7.4.1 增加、修改

    • 设置单个属性

      hset key field value

    • 例1:设置键 user的属性name为itheima

      hset user name itheima

    MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.

    Redis被配置为保存数据库快照,但它目前不能持久化到硬盘。用来修改集合数据的命令不能用

    原因:

    • 强制关闭Redis快照导致不能持久化。

    解决方案:

    • 运行config set stop-writes-on-bgsave-error no 命令后,关闭配置项stop-writes-on-bgsave-error解决该问题。
    • 设置多个属性

      hmset key field1 value1 field2 value2 ...

    • 例2:设置键u2的属性name为itcast、属性age为11

      hmset u2 name itcast age 11

    7.4.2 获取

    • 获取指定键所有的属性

      hkeys key

    • 例3:获取键u2的所有属性

      hkeys u2

    • 获取一个属性的值

      hget key field

    • 例4:获取键u2属性'name'的值

      hget u2 'name'

    • 获取多个属性的值

      hmget key field1 field2 ...

    • 例5:获取键u2属性'name'、'age的值

      hmget u2 name age

    • 获取所有属性的值

      hvals key

    • 例6:获取键'u2'所有属性的值

      hvals u2

    7.4.3 删除

    • 删除整个hash键及值,使用del命令删除属性,属性对应的值会被一起删除
    • hdel key field1 field2 ...

    • 例7:删除键'u2'的属性'age'

      hdel u2 age

    7.5 list类型

    • 列表的元素类型为string
    • 按照插入顺序排序

    7.5.1 增加

    • 在左侧插入数据

      lpush key value1 value2 ...

    • 例1:从键为'a1'的列表左侧加⼊数据a 、 b 、c

      lpush a1 a b c

    • 在右侧插入数据

      rpush key value1 value2 ...

    • 例2:从键为'a1'的列表右侧加⼊数据0 1

      rpush a1 0 1

    • 在指定元素的前或后插入新元素

      linsert key before或after 现有元素 新元素

    • 例3:在键为'a1'的列表中元素'b'前加⼊'3'

      linsert a1 before b 3

    7.5.2 获取

    • 返回列表中指定范围内的元素

      • start、stop为元素的下标索引
      • 索引从左侧开始,第一个元素为0
      • 索引可以是负数,表示从尾部开始计数,如-1表示最后一个元素

      lrange key start stop

    • 例4:获取键为'a1'的列表所有元素

      lrange a1 0 -1

    7.5.3 设置指定索引位置的元素值

    • 索引从左侧开始,第一个元素为0
    • 索引可以是负数,表示尾部开始计数,如-1表示最后一个元素

      lset key index value

    • 例5:修改键为'a1'的列表中下标为1的元素值为'z'

      lset a 1 z

    7.5.4 删除

    • 删除指定元素

      • 将列表中前count次出现的值为value的元素移除
      • count > 0: 从头往尾移除
      • count < 0: 从尾往头移除
      • count = 0: 移除所有

      lrem key count value

    • 例6.1:向列表'a2'中加⼊元素'a'、'b'、'a'、'b'、'a'、'b'

      lpush a2 a b a b a b

    • 例6.2:从'a2'列表右侧开始删除2个'b'

      lrem a2 -2 b

    • 例6.3:查看列表'py12'的所有元素

      lrange a2 0 -1

    7.6 set类型

    • 无序集合
    • 元素为string类型
    • 元素具有唯一性,不重复
    • 说明:对于集合没有修改操作

    7.6.1 增加

    • 添加元素

      sadd key member1 member2 ...

    • 例1:向键'a3'的集合中添加元素'zhangsan'、'lisi'、'wangwu'

      sadd a3 zhangsan sili wangwu

    7.6.2 获取

    • 返回所有的元素

      smembers key

    • 例2:获取键'a3'的集合中所有元素

      smembers a3

    7.6.3 删除

    • 删除指定元素

      srem key

    7.7 zset类型

    • sorted set,有序集合
    • 元素为string类型
    • 元素具有唯一性,不重复
    • 每个元素都会关联一个double类型的score,表示权重,通过权重将元素从小到大排序
    • 说明:没有修改操作

    7.7.1 增加

    • 添加

      zadd key score1 member1 score2 member2 ...

    • 例1:向键'a4'的集合中添加元素'lisi'、'wangwu'、'zhaoliu'、'zhangsan',权重分别为4、5、6、3

      zadd a4 4 lisi 5 wangwu 6 zhaoliu 3 zhangsan

    7.7.2 获取

    • 返回指定范围内的元素
    • start、stop为元素的下标索引
    • 索引从左侧开始,第一个元素为0
    • 索引可以是负数,表示从尾部开始计数,如-1表示最后一个元素

      zrange key start stop

    • 例2:获取键'a4'的集合中所有元素

      zrange a4 0 -1

    • 返回score值在min和max之间的成员

      zrangebyscore key min max

    • 例3:获取键'a4'的集合中权限值在5和6之间的成员

      zrangebyscore a4 5 6

    • 返回成员member的score值

      zscore key member

    • 例4:获取键'a4'的集合中元素'zhangsan'的权重

      zscore a4 zhangsan

    7.7.3 删除

    • 删除指定元素

      zrem key member1 member2 ...

    • 例5:删除集合'a4'中元素'zhangsan'

      zrem a4 zhangsan

    • 删除权重在指定范围的元素

      zremrangebyscore key min max

    • 例6:删除集合'a4'中权限在5、6之间的元素

      zremrangebyscore a4 5 6

    8 与Python交互

    8.1 安装包

    安装Redis的有3种方式https://github.com/andymccurdy/redis-py

    • 第一种:进入虚拟环境py_django,联网安装包redis

      pip install redis

    • 第二种:进入虚拟环境py_django,联网安装包redis

      easy_install redis

    • 第三种:到中文官网-客户端下载redis包的源码,使用源码安装

      一步步执行 wget https://github.com/andymccurdy/redis-py/archive/master.zip
      unzip master.zip
      cd redis-py-master
      sudo python setup.py install

    8.2 调用模块

    • 引入模块

      from redis import *

    • 这个模块中提供了StrictRedis对象(Strict严格),用于连接redis服务器,并按照不同类型提供了不同用法,进行交互操作

    8.3 StrictRedis对象用法

    • 通过init创建对象,指定参数host、port与指定的服务器和端口连接,host默认为localhost,port默认为6379,db默认为0
    sr = StrictRedis(host='localhost', port=6379, db=0)
    
    # 简写
    sr=StrictRedis()
    
    • 根据不同的类型,拥有不同的实例⽅法可以调用,与前面学的redis命令对应,用法需要的参数与命令的参数一致

    string

    • set
    • setex
    • mset
    • append
    • get
    • mget
    • key

    keys

    • exists
    • type
    • delete
    • expire
    • getrange
    • ttl

    hash

    • hset
    • hmset
    • hkeys
    • hget
    • hmget
    • hvals
    • hdel

    list

    • lpush
    • rpush
    • linsert
    • lrange
    • lset
    • lrem

    set

    • sadd
    • smembers
    • srem

    zset

    • zadd
    • zrange
    • zrangebyscore
    • zscore
    • zrem
    • zremrangebyscore

    8.4 对string类型数据进行增删改查

    8.4.1 准备

    • 在桌面上创建redis目录
    • 使用pycharm打开 redis目录
    • 创建redis_string.py文件
    from redis import *
    if __name__=="__main__":
        try:
            #创建StrictRedis对象,与redis服务器建立连接
            sr=StrictRedis()
    
        except Exception as e:
            print(e)
    

    8.4.2 string-增加

    • 用法set,添加键、值,如果添加成功则返回True,如果添加失败则返回False
    • 编写代码如下
    from redis import *
    if __name__=="__main__":
        try:
            #创建StrictRedis对象,与redis服务器建立连接
            sr=StrictRedis()
            #添加键name,值为itheima
            result=sr.set('name','itheima')
            #输出响应结果,如果添加成功则返回True,否则返回False
            print(result)
        except Exception as e:
            print(e)
    

    8.4.3 string-获取

    • 用法get,添加键对应的值,如果键存在则返回对应的值,如果键不存在则返回None
    • 编写代码如下
    from redis import *
    if __name__=="__main__":
        try:
            #创建StrictRedis对象,与redis服务器建立连接
            sr=StrictRedis()
            #获取键name的值
            result = sr.get('name')
            #输出键的值,如果键不存在则返回None
            print(result)
        except Exception as e:
            print(e)
    

    8.4.4 string-修改

    • 用法set,如果键已经存在则进行修改,如果键不存在则进行添加
    • 编写代码如下
    from redis import *
    if __name__=="__main__":
        try:
            #创建StrictRedis对象,与redis服务器建⽴连接
            sr=StrictRedis()
            #设置键name的值,如果键已经存在则进行修改,如果键不存在则进行添加
            result = sr.set('name','itcast')
            #输出响应结果,如果操作成功则返回True,否则返回False
            print(result)
        except Exception as e:
            print(e)
    

    8.4.5 string-删除

    • 用法delete,删除键及对应的值,如果删除成功则返回受影响的键数,否则则返回0
    • 编写代码如下
    from redis import *
    if __name__=="__main__":
        try:
            #创建StrictRedis对象,与redis服务器建立连接
            sr=StrictRedis()
            #设置键name的值,如果键已经存在则进行修改,如果键不存在则进立添加
            result = sr.delete('name')
            #输出响应结果,如果删除成功则返回受影响的键数,否则则返回0
            print(result)
        except Exception as e:
            print(e)
    

    8.4.6 获取键

    • 用法keys,根据正则表达式获取键
    • 编写代码如下
    from redis import *
    if __name__=="__main__":
        try:
            #创建StrictRedis对象,与redis服务器建立连接
            sr=StrictRedis()
            #获取所有的键
            result=sr.keys()
            #输出响应结果,所有的键构成一个列表,如果没有键则返回空列表
            print(result)
        except Exception as e:
            print(e)

    9 主从概念

    • 一个master可以拥有多个slave,一个slave也可以拥有多个slave,如此下去,形成了强大的多级服务器集群架构
    • master用来写数据,slave用来读数据,经统计:网站的读写比率是10:1
    • 通过主从配置可以实现读写分离

    • master和slave都是一个redis实例(redis服务)

    9.1 主从配置

    9.1.1 配置主

    • 查看当前主机的ip地址

      ifconfig

    • 修改etc/redis/redis.conf文件

      sudo vim redis.conf
      bind 192.168.26.128

    • 重启redis服务

      sudo service redis stop
      redis-server redis.conf

    9.1.2 配置从

    • 复制etc/redis/redis.conf文件

      sudo cp redis.conf ./slave.conf

    • 修改redis/slave.conf文件

      sudo vi slave.conf

    • 编辑内容

      bind 192.168.26.128
      slaveof 192.168.26.128 6379
      port 6378

    • redis服务

      sudo redis-server slave.conf

    • 查看主从关系

      redis-cli -h 192.168.26.128 info Replication

    9.2 数据操作

    • 在master和slave分别执行info命令,查看输出信息 进入主客户端

      redis-cli -h 192.168.26.128 -p 6379

    • 进入从的客户端

      redis-cli -h 192.168.26.128 -p 6378

    • 在master上写数据

      set aa aa

    • 在slave上读数据

      get aa

    10 搭建集群

    10.1 简介

    10.1.1 为什么要有集群

    • 之前我们已经讲了主从的概念,一主可以多从,如果同时的访问量过大(1000w),主服务肯定就会挂掉,数据服务就挂掉了或者发生自然灾难
    • 大公司都会有很多的服务器(华东地区、华南地区、华中地区、华北地区、西北地区、西南地区、东北地区、台港澳地区机房)

    10.1.2 集群的概念

    • 集群是一组相互独立的、通过高速网络互联的计算机,它们构成了一个组,并以单一系统的模式加以管理。一个客户与集群相互作用时,集群像是一个独立的服务器。集群配置是用于提高可用性和可缩放性。

     

    当请求到来首先由负载均衡服务器处理,把请求转发到另外的一台服务器上。

    10.2 redis集群

    • 分类
      • 软件层面
      • 硬件层面
    • 软件层面:只有一台电脑,在这一台电脑上启动了多个redis服务。

    • 硬件层面:存在多台实体的电脑,每台电脑上都启动了一个redis或者多个redis服务。

    参考阅读:

    10.3 配置机器

    • 在演示中,127.0.0.1为当前ubuntu机器的ip
    • 在127.0.0.1上进入Desktop目录,创建conf目录
    • 在conf目录下创建文件7000.conf,编辑内容如下

      port 7000
      bind 127.0.0.1
      daemonize yes
      pidfile 7000.pid
      cluster-enabled yes
      cluster-config-file 7000_node.conf
      cluster-node-timeout 15000
      appendonly yes

    复制六份,分别命名为:7000.conf、7001.conf、7002.conf、7003.conf、7004.conf、7005.conf

    把内容中所有的“7000”改为文件名对应的“7001、7002、7003、7004、7005”

    即:六个文件的配置区别在port、pidfile、cluster-config-file三项

    使用配置文件启动redis服务

    redis-server 7000.conf
    redis-server 7001.conf
    redis-server 7002.conf
    redis-server 7003.conf
    redis-server 7004.conf
    redis-server 7005.conf

    查看进程如下图  

    10.4 创建集群

    • redis的安装包中包含了redis-trib.rb,用于创建集群
    • 接下来的操作在127.0.0.1机器上进行
    • 将命令复制,这样可以在任何目录下调用此命令

      sudo cp/usr/local/redis/src/redis-trib.rb /usr/local/bin/
      
    • 安装ruby环境,因为redis-trib.rb是用ruby开发的

      sudo apt-get install ruby

    • 在提示信息处输入y,然后继续安装 

    • 运行如下命令创建集群

      redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
      
    • 执行上述这个指令在某些机器上可能会报错,主要原因是由于安装的 ruby 不是最 新版本!

    • 天朝的防火墙导致无法下载最新版本,所以需要设置 gem 的源

    • 解决办法如下

      -- 先查看 gem 源是什么地址
      gem source -l -- 如果是https://rubygems.org/ 就需要更换
      -- 更换指令为
      gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
      -- 通过 gem 安装 redis 的相关依赖
      sudo gem install redis
      -- 然后重新执行指令
      

      redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
      
    • 提示如下主从信息,输入yes后:

    • 提示完成,集群搭建成功

    10.5 数据验证

    • 根据上图可以看出,当前搭建的主服务器为7000、7001、7002,对应的从服务器是7003、7004、7005
    • 在127.0.0.1机器上连接7002,加参数-c表示连接到集群

      redis-cli -h 127.0.0.1 -c -p 7002

    • 写入数据

      set name itheima

    • 自动跳到了7001服务器,并写入数据成功 

    • 在7003可以获取数据,如果写入数据又重定向到7000(负载均衡) 

    10.6 在哪个服务器上写数据:CRC16

    • redis cluster在设计的时候,就考虑到了去中间化,去中间件,也就是说,集群中 的每个节点都是平等的关系,都是对等的,每个节点都保存各⾃的数据和整个集 群的状态。每个节点都和其他所有节点连接,并且这些连接保持活跃,这样就保 证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据
    • Redis集群没有并使用传统的一致性哈希来分配数据,这是采用另外一种叫做哈希 槽 (hash slot)的方式来分配的。redis cluster 默认分配了 16384 个slot,当我们 set一个key 时,会用CRC16算法来取模得到所属的slot,然后将这个key 分到哈 希槽区间的节点上,具体算法就是:CRC16(key) % 16384。所以我们在测试的 时候看到set 和 get 的时候,直接跳转到了7000端口的节点
    • Redis 集群会把数据存在一个 master 节点,然后在这个 master 和其对应的salve 之间进⾏数据同步。当读取数据时,也根据⼀致性哈希算法到对应的 master 节 点获取数据。只有当一个master 挂掉之后,才会启动一个对应的 salve 节点,充 当 master
    • 需要注意的是:必须要3个或以上的主节点,否则在创建集群时会失败,并且当存 活的主节点数小于总节点数的一半时,整个集群就无法提供服务了

    11 Python交互

    from rediscluster import *
    
    if __name__ == '__main__':
        try:
            # 构建所有的节点,Redis会使用CRC16算法,将键和值写到某个节点上
            startup_nodes = [
                {'host': '127.0.0.1', 'port': '7000'},
                {'host': '127.0.0.1', 'port': '7001'},
                {'host': '127.0.0.1', 'port': '7002'},
                {'host': '127.0.0.1', 'port': '7003'},
                {'host': '127.0.0.1', 'port': '7004'},
                {'host': '127.0.0.1', 'port': '7005'}
            ]
            # 构建StrictRedisCluster对象
            src = StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True)
            # 设置键为name、值为itheima的数据
            result = src.set('name', 'itheima')
            print(result)
            # 获取键为name
            name = src.get('name')
            print(name)
        except Exception as e:
            print(e)
    

     

    展开全文
  • 数据库基础

    2012-09-22 16:27:04
    §11.2.1 什么是PL/SQL? 231 §11.2.1 PL/SQL的好处 232 §11.2.1.1 有利于客户/服务器环境应用的运行 232 §11.2.1.2 适合于客户环境 232 §11.2.1.3 客户及服务器端的好处 232 §11.2.2 PL/SQL 可用的SQL语句 233 ...
  • NOSQL数据库-Redis

    2019-08-15 22:45:12
    NOSQL数据库-Redis 目标 能够理解nosql的概念 能够说出redis的常用数据类型 能够使用redis的string操作命令 能够使用redis的hash操作命令 能够使用redis的list操作...2.1 什么是NOSQL NoSQL(NoSQL = Not Only S...

    NOSQL数据库-Redis

    1. 目标
    • 能够理解nosql的概念
    • 能够说出redis的常用数据类型
    • 能够使用redis的string操作命令
    • 能够使用redis的hash操作命令
    • 能够使用redis的list操作命令
    • 能够使用redis的set操作命令
    • 能够使用jedis对redis进行操作

    反馈:

    1. NOSQL概述

    2.1 什么是NOSQL

    NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。

    2.2 为什么需要NOSQL

    随着互联网的高速崛起,网站的用户群的增加,访问量的上升,传统数据库上都开始出现了性能瓶颈,web程序不再仅仅专注在功能上,同时也在追求性能。所以NOSQL数据库应运而上,具体表现为对如下三高问题的解决:
    
    ·        High performance - 对数据库高并发读写的需求 
    
    web2.0网站要根据用户个性化信息来实时生成动态页面和提供动态信息,所以基本上无法使用动态页面静态化技术,因此数据库并发负载非常高,往往要达到每秒上万次读写请求。关系数据库应付上万次SQL查询还勉强顶得住,但是应付上万次SQL写数据请求,硬盘IO就已经无法承受了。其实对于普通的BBS网站,往往也存在对高并发写请求的需求,例如网站的实时统计在线用户状态,记录热门帖子的点击次数,投票计数等,因此这是一个相当普遍的需求。
    
    ·        Huge Storage - 对海量数据的高效率存储和访问的需求 
    
    类似Facebook,twitter,Friendfeed这样的SNS网站,每天用户产生海量的用户动态,以Friendfeed为例,一个月就达到了2.5亿条用户动态,对于关系数据库来说,在一张2.5亿条记录的表里面进行SQL查询,效率是极其低下乃至不可忍受的。再例如大型web网站的用户登录系统,例如腾讯,盛大,动辄数以亿计的帐号,关系数据库也很难应付。 
    
    ·        High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求 
    
    在基于web的架构当中,数据库是最难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,你的数据库却没有办法像web server和app server那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。对于很多需要提供24小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数据迁移。
    

    2.3 主流的NOSQL产品

    · 键值(Key-Value)存储数据库

    相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
    
    典型应用: 内容缓存,主要用于处理大量数据的高访问负载。 
    
    数据模型: 一系列键值对
    
    优势: 快速查询
    
    劣势: 存储的数据缺少结构化
    

    · 列存储数据库

    相关产品:Cassandra, HBase, Riak
    
    典型应用:分布式的文件系统
    
    数据模型:以列簇式存储,将同一列数据存在一起
    
    优势:查找速度快,可扩展性强,更容易进行分布式扩展
    
    劣势:功能相对局限
    

    · 文档型数据库

    相关产品:CouchDB、MongoDB
    
    典型应用:Web应用(与Key-Value类似,Value是结构化的)
    
    数据模型: 一系列键值对
    
    优势:数据结构要求不严格
    
    劣势: 查询性能不高,而且缺乏统一的查询语法
    

    · 图形(Graph)数据库

    相关数据库:Neo4J、InfoGrid、Infinite Graph
    
    典型应用:社交网络
    
    数据模型:图结构
    
    优势:利用图结构相关算法。
    
    劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。
    

    2.4 NOSQL的特点

    在大数据存取上具备关系型数据库无法比拟的性能优势,例如:
    
    ·        易扩展
    
    NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。也无形之间,在架构的层面上带来了可扩展的能力。
    
    ·        大数据量,高性能
    
    NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
    
    ·        灵活的数据模型
    
    NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的Web2.0时代尤其明显。
    
    ·        高可用
    
    NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如Cassandra,HBase模型,通过复制模型也能实现高可用。
    
    1. Redis概述

    3.1 什么是Redis

    Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s ,且Redis通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
    
    ·        字符串类型 string(常用:json/xml)
    
    ·        散列类型 hash(key value   key--value(map) )
    
    ·        列表类型 list   linkedlist  用户列表
    
    ·        集合类型 set
    
    ·        有序集合类型 sortedset
    

    3.2 Redis的应用场景

    ·        缓存(数据查询、短连接、新闻内容、商品内容等等)
    
    ·        聊天室的在线好友列表
    
    ·        任务队列(开发中常用ActiveMQ 或者 RabbitMQ)(秒杀、抢购、12306等等)
    
    ·        应用排行榜
    
    ·        网站访问统计
    
    ·        数据过期处理(可以精确到毫秒)
    
    ·        分布式集群架构中的session分离
    

    3.3 Redis的特性

    1. window版Redis的安装与使用

    4.1 windows版Redis的下载

    官方提倡使用Linux版的Redis,所以官网值提供了Linux版的Redis下载,我们可以从GitHub上下载window版的Redis,具体链接地址如下:
    
    ·        官网下载地址:http://redis.io/download
    
    ·        github下载地址:https://github.com/MSOpenTech/redis/tags
    

    在今天的课程资料中提供的下载完毕的window版本的Redis:

    4.2 window版Redis的目录结构

    解压Redis压缩包后,见到如下目录机构:

    目录或文件 作用
    redis-benchmark 性能测试工具
    redis-check-aof AOF文件修复工具
    redis-check-dump RDB文件检查工具(快照持久化文件)
    redis-cli 命令行客户端
    redis-server redis服务器启动命令
    redis.windows.conf redis核心配置文件

    4.3 window版Redis的安装与启动

    window版Redis的安装

    window版的安装及其简单,解压Redis压缩包完成即安装完毕

    如果64位不能启动成功,则使用win32-win64版本即可。

    4.4 window版Redis的启动与关闭

    双击Redis目录中redis-server.exe可以启动redis服务,Redis服务占用的端口是6379

    关闭Redis的控制台窗口就可以关闭Redis服务

    4.5 window版Redis的使用

    双击Redis目录中redis-cli.exe可以启动redis客户端

    图形化客户端:

    双击安装 点击Next----finish !

    启动:配置连接即可

    配置信息即可

    localhost 6379 即可

    4.6安装Redis到服务

    打开redis的官方安装文档:

    进入cmd

    服务安装成功,右击启动

    1. Redis的数据类型

    5.1 Redis的5种数据类型

    redis是一种高级的key-value的存储系统,其中value支持五种数据类型:
    
    ·        字符串(**String)**
    
    ·        哈希(**hash)**
    
    ·        字符串列表(**list)**
    
    ·        字符串集合(**set)**
    
    ·        有序字符串集合(sorted set)
    
    在日常开发中主要使用比较多的有字符串、哈希、字符串列表、字符串集合四种类型,其中最为常用的是字符串类型。
    
     关于key的定义,注意如下几点:
    
    ·        key不要太长,最好不要超过1024个字节,这不仅会消耗内存还会降低查找效率
    
    ·        key不要太短,如果太短会降低key的可读性 
    
    ·        在项目中,key最好有一个统一的命名规范
    

    5.2 字符串类型string

    使用场景:

    常规key-value缓存应用。常规计数: 微博数, 粉丝数,json格式的数据。
    

    5.2.1 字符串类型string概述

    字符串类型是Redis中最为基础的数据存储类型,字符串在Redis中是二进制保存,因此是安全的,这便意味着该类型存入和获取的数据相同。
    
    在Redis中字符串类型的Value最多可以容纳的数据长度是512M。
    

    5.2.2 字符串类型string常用命令

    · 5.2.2.1 set key value

    设定key持有指定的字符串value,如果该key存在则进行覆盖操作。总是返回”OK”

    127.0.0.1:6379> set company "itcast"
    
    OK
    
    127.0.0.1:6379>
    

    · 5.2.2.2 get key

    获取key的value。如果与该key关联的value不是String类型,redis将返回错误信息,因为get命令只能用于获取String value;如果该key不存在,返回(nil)。

    127.0.0.1:6379> get company
    
    "itcast"
    

    · 5.2.2.3 del key

    删除指定key

    127.0.0.1:6379> del company
    
    (integer) 1
    
    127.0.0.1:6379> get company
    
    (nil)
    

    · 5.2.2.4 INCR、INCRBY、DECR、DECRBY

    当存储的key对应的值是一个整数时,让当前的key对应的值进行递增。并返回递增后的值。

    语法:

    INCR key:给key的值递增1
    
    INCRBY key increment:给key的值增加指定的整数(increment)
    
    DECR key:给key的值递减1
    
    DECRBY key decrement:给key的值递减指定的整数(decrement)
    

    用途:数据存储于一个库的一张表可以达到id的自增长,但当数据量非常庞大,不得不存储于多个表甚至于多个库时,如果使用数据库的id自增长就存在问题,多个表或者多个库中的不同数据的id会重复,因为每个表的id都是从1自增长的。这就需要借助于其他手段或者机制来实现,比如:借助于redis的INCR自增长的机制,维护共同的id

    127.0.0.1:6379> set num 1
    
    OK
    
    127.0.0.1:6379> incr num
    
    (integer) 2
    
    127.0.0.1:6379> incrby num 2
    
    (integer) 4
    
    127.0.0.1:6379> decr num
    
    (integer) 3
    
    127.0.0.1:6379> decrby num 2
    
    (integer) 1
    

    · 5.2.2.5 APPEND

    Append 命令用于为指定的 key 追加值。

    1) 如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。

    2) 如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。

    语法:

    APPEND key value

    返回值:

    追加指定值之后, key 中字符串的长度。

    127.0.0.1:6379> set aaa bbb
    OK
    127.0.0.1:6379> append aaa ccc
    (integer) 6
    127.0.0.1:6379> get aaa
    "bbbccc"
    127.0.0.1:6379> append ddd ddd
    (integer) 3
    127.0.0.1:6379> get ddd
    "ddd"
    

    · 5.2.2.6 STRLEN

    Strlen 命令用于获取指定 key 所储存的字符串值的长度。

    语法:

    STRLEN key
    

    返回值:

    key的值字符串长度,如果key不存在,返回0
    
    127.0.0.1:6379> get aaa
    "bbbccc"
    127.0.0.1:6379> strlen aaa
    (integer) 6
    127.0.0.1:6379> get ccc
    (nil)
    127.0.0.1:6379> strlen ccc
    (integer) 0
    

    · 5.2.2.7 MGET MSET

    Mset 命令用于同时设置一个或多个 key-value 对。

    语法:

    MSET key1 value1 key2 value2 ... keyN valueN
    

    Mget 命令返回所有(一个或多个)给定 key 的值。 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。

    语法:

    MGET KEY1 KEY2 .. KEYN
    
    127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
    OK
    127.0.0.1:6379> mget k1 k2 k3
    1) "v1"
    2) "v2"
    3) "v3"
    

    5.3 哈希类型hash

    5.3.1. 哈希类型hash概述

    Key是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 
    也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题
    

    5.3.2. 哈希类型hash常用命令

    使用场景:

    存储部分变更数据,如用户信息等。 
    

    · 5.3.2.1 hset key field value

    为指定的key设定field/value对(键值对)。

    127.0.0.1:6379> hset myhash username haohao
    
    (integer) 1
    

    · 5.3.2.2 hget key field

    返回指定的key中的field的值

    127.0.0.1:6379> hset myhash username haohao
    
    (integer) 1
    
    127.0.0.1:6379> hget myhash username
    
    "haohao"
    

    · 5.3.2.3 hdel key field [field … ]

    可以删除一个或多个字段,返回值是被删除的字段个数

    127.0.0.1:6379> hdel myhash username 
    
    (integer) 1
    

    · 5.3.2.4 HGETALL、HKEYS、HVALS 批量查询

    1、HGETALL获取在哈希表中指定 key 的所有字段和值

    语法:HGETALL key

    返回值:

    以列表形式返回哈希表的域和域的值。若 key 不存在,返回空列表。在返回值里,紧跟每个域名(field name)之后是域的值(value)

    127.0.0.1:6379> hset myhash username 123
    (integer) 1
    127.0.0.1:6379> hset myhash age 18
    (integer) 1
    127.0.0.1:6379> hset myhash sex male
    (integer) 1
    127.0.0.1:6379> hgetall myhash
    1) "username"
    2) "123"
    3) "age"
    4) "18"
    5) "sex"
    6) "male"
    127.0.0.1:6379>
    

    2、HKEYS获取所有哈希表中的字段

    语法:

    HKEYS key

    返回值:

    一个包含哈希表中所有字段的表。当 key 不存在时,返回一个空表。

    127.0.0.1:6379> hkeys myhash
    1) "username"
    2) "age"
    3) "sex"
    

    3、HVALS获取哈希表中所有值

    语法:

    HVALS key

    返回值:

    一个包含哈希表中所有域(field)值的列表。 当 key 不存在时,返回一个空表

    127.0.0.1:6379> hvals myhash
    1) "123"
    2) "18"
    3) "male"
    

    · 5.3.2.5 HMSET、HMGET(批量新增)

    HMSET、HMGET这两个命令与MSET和MGET类似,都是批量的添加和获取

    语法:

    HMSET key field1 value1 [field2 value2 ] 
    

    同时将多个 field-value (域-值)对设置到哈希表 key 中。此命令会覆盖哈希表中已存在的字段。

    返回值:

    如果命令执行成功,返回 OK 。当 key 不是哈希表(hash)类型时,返回一个错误。
    
    127.0.0.1:6379> hmset myhash qq 123456 email 123456@qq.com  address china
    OK
    127.0.0.1:6379> hgetall myhash
     1) "username"
     2) "123"
     3) "age"
     4) "18"
     5) "sex"
     6) "male"
     7) "qq"
     8) "123456"
     9) "email"
    10) "123456@qq.com"
    11) "address"
    12) "china"
    

    语法:

    HMGET key field [field ...]
    
    获取所有给定字段的值
    

    返回值:

    一个包含多个给定域的关联值的表,表值的排列顺序和给定域参数的请求顺序一样。
    

    如果给定的字段不存在于哈希表,那么返回一个 nil 值。

    因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。

    127.0.0.1:6379> hmget myhash username qq email
    1) "123"
    2) "123456"
    3) "123456@qq.com"
    

    5.4. 列表类型list

    应用场景:

    Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。
    
    List 就是链表,相信略有数据结构知识的人都应该能理解其结构。使用List结构,我们可以轻松地实现最新消息排行等功能。List的另一个应用就是消息队列,
    
    可以利用List的PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作List中某一段的api,你可以直接查询,删除List中某一段的元素。
    

    5.4.1. 列表类型list概述

    在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。

    在插入时,如果该键不存在,Redis将为该键创建一个新的链表。

    如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。

    List中可以包含的最大元素数量是4294967295

    5.4.2. 列表类型list

    · lpush key value1 value2 …

    在指定的key所关联的list的头部插入所有的values,

    如果该key不存在,该命令在插入的之前创建一个与该key关联的空链表,之后再向该链表的头部插入数据。

    插入成功,返回元素的个数。

    127.0.0.1:6379> lpush mylist a b c
    
    (integer) 3
    
    127.0.0.1:6379>
    

    · rpush key value value value

    在指定的key对应的list的尾部插入所有的value,

    如果该key不存在,该命令在插入之前创建一个与该key对应的空链表,再从尾部插入数据。

    插入成功,返回元素的个数。

    · lpop key

    返回并弹出指定的key关联的链表中的第一个元素,即头部元素。

    如果该key不存在,返回nil;

    若key存在,则返回链表的头部元素。

    127.0.0.1:6379> lpush mylist a b c
    
    (integer) 3
    
    127.0.0.1:6379> lpop mylist
    
    "c"
    
    127.0.0.1:6379> lpop mylist
    
    "b"
    

    · rpop key

    从尾部弹出元素。

    127.0.0.1:6379> lpush mylist a b c
    
    (integer) 3
    
    127.0.0.1:6379> rpop mylist
    
    "a"
    

    5.5. 集合类型set

    应用场景:

         Redis set对外提供的功能与list类似是一个列表的功能,
         特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,
         set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,
         这个也是list所不能提供的。
    

    5.5.1. 集合类型set

    在Redis中,我们可以将Set类型看作为没有排序的字符集合,和List类型一样,我们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要说明的是,这些操作的时间复杂度为O(1),即常量时间内完成次操作。Set可包含的最大元素数量是4294967295,和List类型不同的是,Set集合中不允许出现重复的元素。

    5.5.2. 集合类型set的常用命令

    · sadd key values[value1、value2…]

    向set中添加数据,如果该key的值已有则不会重复添加

    127.0.0.1:6379> sadd myset a b c
    
    (integer) 3
    

    · smembers key

    获取set中所有的成员

    127.0.0.1:6379> sadd myset a b c
    
    (integer) 3
    
    127.0.0.1:6379> smembers myset
    
    1) "c"
    
    2) "a"
    
    3) "b"
    

    · srem key members[member1、member2…]

    删除set中指定的成员

    127.0.0.1:6379> srem myset a b
    
    (integer) 2
    
    127.0.0.1:6379> smembers myset
    
    1) "c"
    
    127.0.0.1:6379>
    
    
    小结:
    字符串   set   get    del  (json格式字符串)
    hash    hset  hget   hgetall  hmset
    list    lpush  rpush  lpop  rpop 
    set     sadd  smemebers   srem
    
    1. Redis的通用命令

    6.1 keys pattern

    获取所有与pattern匹配的key,返回所有与该key匹配的keys。*表示任意一个或多个字符,?表示任意一个字符

    127.0.0.1:6379> keys *
    
    1) "company"
    
    2) "mylist"
    
    3) "myhash"
    
    4) "myset"
    

    6.2del key1 key2…

    删除指定的key

    127.0.0.1:6379> del company
    
    (integer) 1
    

    6.3 exists key

    判断该key是否存在,1代表存在,0代表不存在

    127.0.0.1:6379> exists compnay
    
    (integer) 0
    
    127.0.0.1:6379> exists mylist
    
    (integer) 1
    
    127.0.0.1:6379>
    

    6.4type key

    获取指定key的类型。该命令将以字符串的格式返回。 返回的字符串为string、list、set、hash,如果key不存在返回none

    127.0.0.1:6379> type company
    
    string
    
    127.0.0.1:6379> type mylist
    
    list
    
    127.0.0.1:6379> type myset
    
    set
    
    127.0.0.1:6379> type myhash
    
    hash
    
    127.0.0.1:6379>
    
    1. Jedis的基本使用

    7.1. jedis的介绍

    • jedis是官方首选的java客户端开发包

      Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。

      在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。

      在企业中用的最多的就是Jedis,Jedis同样也是托管在github上,

      地址:https://github.com/xetorthio/jedis。
      下载jedis解压后得到jar包如下:java操作redis数据库API(Jedis)

    7.2. jedis的基本操作

    7.2.1. jedis常用API

    方法 解释
    new Jedis(host, port) 创建jedis对象,参数host是redis服务器地址,参数port是redis服务端口
    set(key,value) 设置字符串类型的数据
    get(key) 获得字符串类型的数据
    hset(key,field,value) 设置哈希类型的数据
    hget(key,field) 获得哈希类型的数据
    lpush(key,values) 设置列表类型的数据
    lpop(key) 列表左面弹栈
    rpop(key) 列表右面弹栈
    del(key) 删除指定的key

    7.2.2. jedis的基本操作

    导入开发包:

    public void testJedisSingle(){
    
        //1 设置ip地址和端口
    
        Jedis jedis = new Jedis("localhost", 6379);
    
        //2 设置数据
    
        jedis.set("name", "itheima");
    
        //3 获得数据
    
        String name = jedis.get("name");
    
        System.out.println(name);
    
        //4 释放资源
    
        jedis.close();
    
    }
    

    7.3. jedis连接池的使用

    7.3.1. jedis连接池的基本概念

    jedis连接资源的创建与销毁是很消耗程序性能,所以jedis为我们提供了jedis的池化技术,

    jedisPool在创建时初始化一些连接资源存储到连接池中,使用jedis连接资源时不需要创建,

    而是从连接池中获取一个资源进行redis的操作,使用完毕后,不需要销毁该jedis连接资源,

    而是将该资源归还给连接池,供其他请求使用。

    7.3.2. jedisPool的基本使用

    public void testJedisPool(){
    
        //1 获得连接池配置对象,设置配置项
    
        JedisPoolConfig config = new JedisPoolConfig();
    
        // 1.1 最大连接数
    
        config.setMaxTotal(30);
    
        // 1.2  最大空闲连接数
    
        config.setMaxIdle(10);
    
        
    
        //2 获得连接池
    
        JedisPool jedisPool = new JedisPool(config, "localhost", 6379);
    
        
    
        //3 获得核心对象
    
        Jedis jedis = null;
    
        try {
    
            jedis = jedisPool.getResource();
    
            
    
            //4 设置数据
    
            jedis.set("name", "itcast");
    
            //5 获得数据
    
            String name = jedis.get("name");
    
            System.out.println(name);
    
            
    
        } catch (Exception e) {
    
            e.printStackTrace();
    
        } finally{
    
            if(jedis != null){
    
                jedis.close();
    
            }
    
            // 虚拟机关闭时,释放pool资源
    
            if(jedisPool != null){
    
                jedisPool.close();
    
            }
    
        }
    
    }
    

    7.4. 案例-编写jedis连接池工具类

    JedisUtils.java

    package util;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    import java.util.ResourceBundle;
    
    /**
     * Jedis工具类
     */
    public final class JedisUtil {
    	private JedisUtil(){}
    	private static JedisPool jedilPool;
    	private static int maxtotal;
    	private static int maxwaitmillis;
    	private static String host;
    	private static int port;;
    	
    	/**
    	 * 读取jedis.properties配置文件
    	 */
    	static{
    		ResourceBundle rb = ResourceBundle.getBundle("jedis");
    		maxtotal = Integer.parseInt(rb.getString("maxtotal"));
    		maxwaitmillis = Integer.parseInt(rb.getString("maxwaitmillis"));
    		host = rb.getString("host");
    		port = Integer.parseInt(rb.getString("port"));
    	}
    	/**
    	 * 创建连接池
    	 */
    	static{
    		JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    		jedisPoolConfig.setMaxTotal(maxtotal);
    		jedisPoolConfig.setMaxWaitMillis(maxwaitmillis);
    		jedilPool = new JedisPool(jedisPoolConfig,host,port);
    	}
    	/**
    	 * 获取Jedis
    	 */
    	public static Jedis getJedis(){
    		return jedilPool.getResource();
    	}
    	/**
    	 * 关闭Jedis
    	 */
    	public static void close(Jedis jedis){
    		if(jedis!=null){
    			jedis.close();
    		}
    	}
    }
    

    jedis.properties(src目录下配置文件,编写配置文件)

    maxtotal=100
    maxwaitmillis=3000
    host=127.0.0.1
    port=6379
    

    如何快速读取properties配置文件信息

    使用jdk提供 ResourceBundle加载 通过getBundle(“文件名”) 通过 getString(“key”)获取目标数据

    src下添加Properties配置文件

    内容:

    username=lisi
    password=123
    url=http://xxx
    driverclass=com.mysql.jdbc.Driver
    

    加载代码实现 : ResourceBundle.java使用说明

    package cn.itheima.jedis.demo;
    
    import java.util.ResourceBundle;
    
    public class PropertiesDemo {
        //  java  专属类   ResourceBundle 对象  jdk 提供
        public static void main(String[] args) {
            //  getBundle源码查询 src路路径下的properties文件 传递文件名默认  .properties 所有不需要写扩展名
            String s = ResourceBundle.getBundle("db").getString("driverclass");
            String url = ResourceBundle.getBundle("db").getString("url");
            String aa = ResourceBundle.getBundle("db").getString("username");
            String bb = ResourceBundle.getBundle("db").getString("password");
            System.out.println(s);
            System.out.println(url);
            System.out.println(aa);
            System.out.println(bb);
        }
    }
    
    1. 案例:Redis实战之查询所有省份(重点)

    需求:访问index.html页面,使用ajax请求加载省份列表,用户第一次访问数据库获取,以后都从redis里面获取。

    资料:准备省市区表和数据

    表设计说明:

    CREATE TABLE `pcd` (
      `id` int(11) NOT NULL,
      `pid` int(11) DEFAULT NULL,
      `name` varchar(10) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    案例分析:

    代码实现:

    1. 搭建项目环境

    导入jar

    mysql c3p0 jdbcTemplate redis /pool / fastjson

    配置文件 src

    : c3p0 / jedis 连接池配置

    工具类: JDBCUtils / JedisUtils

    实体类

    package cn.itheima.jedis.domain;
    
    public class City {
    
        private int id;
        private int pid;
        private String name;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public int getPid() {
            return pid;
        }
    
        public void setPid(int pid) {
            this.pid = pid;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    首页

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h2>省市区三级联动</h2>
    
    省&nbsp;
    <select name="province">
        <option value="">--请选择--</option>
    
    </select>
    
    市&nbsp;
    <select name="city">
        <option value="">--请选择--</option>
    
    </select>
    
    区&nbsp;
    <select name="area">
        <option value="">--请选择--</option>
    
    </select>
    
    </body>
    </html>
    

    服务器端代码开发:index.html页面 导入jquery脚本

    web层代码实现

       protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String pid = request.getParameter("pid");
            PcdService pcdService = new PcdServiceImpl();
            String jsonString = pcdService.loadCityDataByPid(Integer.parseInt(pid));
            response.setContentType("text/json;charset=utf-8");
            response.getWriter().print(jsonString);
        }
    

    service

    redis查询数据,如为null 查询mysql数据库,将数据存放到redis中
    public class PcdServiceImpl implements PcdService {
        private PcdDao pcdDao = new PcdDaoImpl();
    
        @Override
        public String loadCityDataByPid(int pid) {
            //1  先查询redis ---null  才会查询 mysql数据库,将数据json格式字符串 保存redis中
            Jedis jedis = JedisUtils.getJedis();
            String jsonString = jedis.get("cityData");//  不为null  直接从redis 去数据即可
            if (jsonString == null) {
                // redis没有保存省市区信息  查询数据库
                List<City> cityList = pcdDao.findCityDataByPid(pid);
                jsonString = JSON.toJSONString(cityList);
                jedis.set("cityData", jsonString);
    
            }
            JedisUtils.close(jedis);
    
            return jsonString;
        }
    }
    

    dao

    public class PcdDaoImpl implements PcdDao {
        @Override
        public List<City> findCityDataByPid(int pid) {
            System.out.println("查询数据库一次-----");
            String sql ="select * from pcd where pid = ?";
            JdbcTemplate template = new JdbcTemplate(C3p0Utils.getDataSource());
            List<City> cityList = template.query(sql,new BeanPropertyRowMapper<>(City.class),pid);
            return cityList;
        }
    }
    

    测试:mysql数据库服务停止!

    第一次查询 数据从Mysql数据库。后续查询不需要数据库查询了!

    9 redis的持久化机制(拓展)

    9.1 RDB持久化机制

    默认使用的是RBD持久化机制

    redis的默认的持久化机制

    save 900 1 15分钟内1条数据发生了变化,那么就进行持久化
    save 300 10 5份钟内10条件数据发生了变化,那么就进行持久化
    save 60 10000 1分钟内,10000条数据发生了变化,就进行持久化

    默认的RDB持久化机制,保存数据的文件

    dbfilename dump.rdb

    当redis重新启动的时候,redis会将文件中的所有的数据再次重新加载到内存中.

    9.2 AOF持久化机制

    appendonly no 表示不采用AOF持久化机制

    如果改为yes,那么表示采用AOF持久化机制.

    appendfsync always 没当数据发生变化,就进行持久化

    appendfsync everysec 每秒进行持久化

    appendfsync no 不进行持久化.

    AOF持久化机制默认的保存数据的文件

    appendfilename “appendonly.aof” 以命令的形式保存文件,redis每次重启,都会将这个文件中的命令执行一遍.

    redis穿透

    redis雪崩

    redis哨兵模式5

    小结

    1. redis安装和启动(会操作)
    2. 掌握redis存储string,list,hash数据。
    3. jedisPool使用和工具类编写.(会使用)
    4. redis案例-熟练
    5. 作业: 要求1.基于redis缓存完成查询商品导航分类信息的展示。要求 2. 选择一个商品类别,显示对应的商品列表信息。
    展开全文
  • Redis高性能缓存数据库Redis 基础入Redis 介绍特性使用场景正确安装与启动重要的指令使用全局命令单线程架构字符串 - String内部编码应用场景哈希 - Hash内部编码应用场景三种方案实现用户信息存储优缺点列表 - List...
  • NOSQL数据库——Redis

    2020-02-15 18:40:56
    文章目录什么是RedisLinux版的Redis简单使用Redis的数据类型key:String类型value:支持五种数据类型Redis的数据操作(待完善)string类型常用命令hash类型常用命令list类型常用命令set类型的常用命令Redis的通用...
  • oracle数据库经典题目

    2011-02-17 15:05:20
    14. 使用什么命令可以清除表中所有的内容? ( D ) A.INSERT B.UPDATE C. DELETE D.TRUNCATE 15.关于模式的描述下列哪一项不正确?( C ) A.表或索引等模式对象一定属于某一个模式 B.在Oracle数据库中,模式与...
  • whatis命令是用于查询一个命令执行什么功能,并将查询结果打印到终端上。 whatis命令在用catman -w命令创建的数据库中查找command参数指定的命令、系统调用、库函数或特殊文件名。whatis命令显示手册部分的页眉行。...
  • 实务5 oracle安装完成后,用户sys与system默认的密码是什么 实务 实务6 如何找到oracle安装时生成的日志文件 实务7 安装oracle时,是否需要安装jdk 实务8 解压安装文件时,报告无权限 实务 实务9 安装oracle软件...
  • 实务5 oracle安装完成后,用户sys与system默认的密码是什么 实务 实务6 如何找到oracle安装时生成的日志文件 实务7 安装oracle时,是否需要安装jdk 实务8 解压安装文件时,报告无权限 实务 实务9 安装oracle软件...
  • 概念: redis一款高性能的NOSQL系列的非关系型数据库1.1.什么是NOSQL1.2.主流的NOSQL产品1.3 什么是Redis2. 下载安装3. 命令操作1. redis的数据结构:2. 字符串类型 string3. 哈希类型 hash4. 列表类型 list:可以...
  • RedisNosql什么是NosqlNosql产品的特点大数据时代的3V+3高Nosql产品的四大类什么是redisredis的特点安装redis基础命令五大数据类型String(字符串)List(列表) Nosql 什么是Nosql Nosql(Not only sql) 不仅仅SQL...
  • 文章目录 ... 二、下载安装三、命令操作3.1 数据结构3.2 字符串类型 string3.3 哈希类型 hash3.4 列表类型 list3.5 集合类型 set3.6 有序结合 sortedset3.7 通用命令 四、持久化操作4.1 redis持...
  • [NOSQL数据库]——Redis

    2019-06-20 09:37:08
    1.1.什么是NOSQL 1.2.主流的NOSQL产品 1.3 什么是Redis 二、下载安装 三、数据结构介绍 四、常用命令操作 1. redis的数据结构: 2. 字符串类型 string 3. 哈希类型 hash 4. 列表类型 list:可以添加一个元素...
  • 1.首先要知道数据库使用的是什么归档模式:  使用命令: archive log list  2。从非归档模式转换为归档模式的步骤:  (1) 关闭数据库 shutdown nomal/immediate/transactional 不能使用a
  • 存储结构是什么?`` 俩种持久方式:AOF和RDB AOF: RDB: 存储结构:内容是redis通讯协议(RESP )格式的命令文本存储。 3.redis支持那些架构? 单点:架构简单,但是内存容量有限 、处理能力有限、无法高可用 主...
  • Redis常用命令

    2021-03-02 19:22:48
    1.什么是redis? Redis(Remote Dictionary Server ),即远程字典服务,C语言编写的,可基于内存也可持久化的日志型、Key-Value数据库一款高性能的非关系型数据库。 2.redis 的数据结构 redis存储的 key:value...
  • Oracle8i_9i数据库基础

    2010-03-03 14:16:58
    §11.2.1 什么是PL/SQL? 231 §11.2.1 PL/SQL的好处 232 §11.2.1.1 有利于客户/服务器环境应用的运行 232 §11.2.1.2 适合于客户环境 232 §11.2.1.3 客户及服务器端的好处 232 §11.2.2 PL/SQL 可用的SQL语句 233 ...
  • 1、什么是事务控制 2、事务的四个特性 3、MySQL的事务控制 4、提交事务 5、事务回滚 6、什么是分区表 7、分区表的好处 8、分区表的四种类型 9、Range分区 10、List分区 11、分区的其他操作 12、什么是...
  • §1.5.4 LIST(列出)命令..................................................................................................33 §1.5.5 Change(替换字符串)命令..................................................
  • 概念: redis一款高性能的NOSQL系列的非关系型数据库1.2.主流的NOSQL产品1.3 什么是Redis2. 下载安装3. 命令操作1. redis的数据结构:2. 字符串类型 string3. 哈希类型 hash4. 列表类型 list:可以添加一个元素到...
  • redis入门概念NOSQL和关系型数据库比较非 / 关系型数据库的优势什么是Redis和Redis支持的键值数据类型redis的应用场景命令操作redis的数据结构字符串类型 string哈希类型 hash列表类型 list集合类型 set有序集合类型...
  • redis概念须知一、 分布式数据库CAP原理二、 redis2.1 redis特性2.2 redis基础命令2.3 redis数据结构2.3.1 String2.3.2 List2.3.3 Set2.3.4 Hash2.3.5 ZSet2.4 redis配置文件 一、 分布式数据库CAP原理 传统的ACID...
  • redis常用命令

    2021-03-19 14:03:15
    一、Redis是什么 1、redis 完全开源 ,高性能的Key-value 内存数据库; 高性能:能读的速度是110000次/s,写的速度是81000次/s; 原子性:对键值得操作只有成功或者不成功; 数据类型多:支持strings(字符串...
  • redis 常用命令集合

    2020-05-26 09:37:33
    redis是什么 1.基于内存的key-value数据库 2.基于c语言编写的,可以支持多种语言的api //set每秒11万次,取get 81000次 3.支持数据持久化 4.value可以是string,hash, list, set, sorted set redis使用场景 去...
  • redis 之相关命令

    2018-07-13 16:47:00
    什么缓存数据库更要首选redis?如何使用redis? 一、使用缓存数据库什么首选用redis? 我们都知道,把一些热数据存到缓存中可以极大的提高速度,那么问题来了,用Redis好还是Memcached好呢,以下它们...
  • 什么是Redis

    2015-06-11 11:02:05
    Redis开源的,基于BSD协议,既可基于内存亦可持久化的key-value数据库。它可以存储 strings,hashes,lists,sorted sets,bitmaps和hyperloglogs。 你可以对这些数据进行原子操作,例如向string中追加内容,向list...
  • 目录1.Redis简介1.1 NoSQL1.2 NoSQL的类别1.3 Redis是什么1.4 Redis优缺点1.Redis优势2.Redis缺点2.Redis常用命令2.1 String类型2.2 Hash类型2.3 List类型2.4 Set类型2.5 Zset3.使用4.实际应用4.1 导入config4.2 ...

空空如也

空空如也

1 2 3 4 5 ... 17
收藏数 339
精华内容 135
关键字:

数据库list是什么命令