Redis的入门

vampire 2020年09月15日 21次浏览

NoSQL数据库简介

  • 解决功能性的问题

    Java, Jsp, RDBMS, Tomcat, HTML, Linux, Jdbc, SVN
    
  • 解决扩展性的问题

    Structs, Spring, SpringMVC, Hibernate, Mybatis
    
  • 解决性能的问题

    NoSQL, Java线程, Hadoop, Nginx, MQ, ElasticSearch
    
  • 简述

    * 不依赖业务逻辑方式存储,简单的key-value模式存储
    * 不遵循SQL标准
    * 不支持ACID
    * 远超于SQL的性能
    
  • 适用场景

    * 对数据高并发的读写
    * 海量数据的读写
    * 对数据高可扩展性
    
  • 不适用场景

    * 需要事务支持
    * 基于sql的机构化查询存储,处理复杂的关系,需要即席查询
    
  • 数据库对比

    * Memcached
    	* 很早出现的NoSql数据库
    	* 数据都在内存中,一般不持久化
    	* 支持简单的key-value模式
    	* 一般是作为缓存数据库辅助持久化的数据库
    	* 多线程+锁
    * Redis
    	* 几乎覆盖了Memcached的绝大部分功能
        * 数据都在内存中,支持持久化,主要用作备份恢复
        * 除了支持简单的key-value模式,还支持多种数据结构的存储,比如 list、set、hash、zset等。
        * 一般是作为缓存数据库辅助持久化的数据库
        * 单线程+多路IO复用
    * mongoDB
    	* 高性能、开源、模式自由(schema  free)的文档型数据库
        * 数据都在内存中, 如果内存不足,把不常用的数据保存到硬盘
        * 虽然是key-value模式,但是对value(尤其是json)提供了丰富的查询功能
        * 支持二进制数据及大型对象
        * 可以根据数据的特点替代RDBMS ,成为独立的数据库。或者配合RDBMS,存储特定的数据。
    * 列式数据库(HBase)
    * Cassandra
    	* 庞大集群上的海量数据集
    * 图关系型数据库(Neo4j)
    	* 社会关系,公共交通网络,地图及网络拓谱
    

Redis的介绍及安装启动

* 配合关系型数据库做高速缓存
	1. 高频次,热门访问的数据,降低数据库IO
	2. 分布式架构,做session共享
  • 存储特定数据

    • 最新N个数据

      通过List实现按自然时间排序的数据

    • 排行榜,TopN

      利用zset(有序集合)

    • 时效性的数据,比如手机验证码

      Expire过期

    • 计数器,秒杀

      原子性,自增方法INCR,DECR

    • 去除大量数据中的重复数据

      Set集合

    • 构建队列

      利用list集合

    • 发布订阅消息系统

      pub/sub模式

  • Redis是单线程+多路I/O复用技术

       多路复用是指使用一个线程来检查多个文件描述符(Socket)的就绪状态,比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)。
    

Redis的五大数据类型

  • key

    • keys * 查询当前库的所有键
    • exists <key> 查询某个键是否存在
    • type <key> 查看键的类型
    • del <key> 删除某个键
    • expire <key> <seconds> 为键值设置过期时间,单位秒
    • ttl <key> 查看还有多少秒过期, -1表示永不过期, -2表示已过期
    • dbsize 查看当前键的数量
  • String

    1. String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
    2. String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
    3. String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M
    
    • 方法

      get key: 查询对应键值
      set key value: 添加键值对
      append key value: 将给定value添加到原值的末尾
      strlen key: 获得值得长度
      setnx key value: key不存在时,设置key的值
      incr key: 将key中存储的数字值增1,只能对数字值操作,如果为空,则新增值为1
      decr key: 将key中存储的数字值减1,只能对数字值操作,如果为空,则新增值为-1
      incrby/decrby key 步长: 指定步长增减
      mset key1 value1 key2 value2 ……:同时设置多个k-v对
      mget key1 key2 key3: 同时获取多个value
      msetnx key1 value1 key2 value2 ……: 当且仅当所有key都不存在时设置
      getrange key <起始位置> <结束位置>: 获得值得范围
      setrange key <起始位置> <value>: 从起始位置开始用value覆盖值
      setex key 过期时间 value:同时设置值和过期时间 
      getset key value: 获取旧值设置新值
      
  • List

    * 单键多值
    	Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
        它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
    
    • 方法

      * lpush/rpush key value1 value2 value3
      	从左边/右边插入一个或多个值
      * lpop/rpop key
      	从左边/右边吐出一个值。
      	值在键在,值亡键亡
      * rpoplpush key1 key2
      	从key1右边吐出一个值,插到key2左边
      * lrange key start stop
      	按照索引下标获取元素
      * lrange key index
      * llen key: 长度
      * linsert key before value newvalue: 在value前面插入newvalue
      * lrem key n value
      	从左边删除n个value
      
  • set

    * Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。
    
    • 方法

      * sadd key value1 value2 ……
      	添加
      * smembers key
      	取出该集合的所有值
      * sismember key vlaue
      	key是否含有该value值,有返回1,无返回0
      * scard key
      	返回集合的元素个数
      * srem key value1 value2 ……
      	删除集合元素
      * spop key
      	随机吐出集合中一个值
      * srandmember key n
      	随机取出n个值,不会从集合中删除
      * sinter key1 key2
      	交集
      * sunion key1 key2
      	并集
      * sdiff key1 key2
      	差集
      
  • hash

    * Redis  hash 是一个键值对集合。
    * Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
    * 类似Java里面的Map<String,Object>
    
    • 方法

      * hset key field value
      	给key集合中field键值赋值value
      * hget key1 field
      	取出键值
      * hmget key1 field1 value1 field2 value2
      	批量设置hash的值
      * hexists key field
      	查看是否field存在
      * hkeys key
      	列出key中的所有field
      * hvals key
      	列出所有value
      * hincrby key filed increment
      	为field对应的值加上增量increment
      * hsetnx key field value
      	当且仅当field不存在时设置
      
  • zset (sorted set)

    * 因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
    
    • 方法

      * zadd key score1 value1 score2 value
      	添加
      * zrange key start stop WITHSCORES
      	返回start到stop之间的元素, 带上分数
      * zrangebyscore key min max WITHSCORE LIMIT offset count
      	返回介于min和max的成员(包括边界),按从小到大排序
      * zrevrangebyscore key max min WITHSCORE LIMIT offset count
      	返回介于min和max的成员(包括边界),按从大到小排序
      * zincrby key increment value
      	为元素的score加分
      * zrem key value
      	删除指定元素的值
      * zcount key min max
      	统计分数区间的个数
      * zrank key value
      	返回该值的排名。从0开始
      

Redis的Java客户端Jedis

  • 导入jar包

    • Commons-pool-1.6.jar
    • Jedis-2.1.0.jar
  • 配置linux

    • 禁用Linux的防火墙:
    • 临时禁用:service iptables stop
    • 关闭开机自启:chkconfig iptables off
    • redis.conf中注释掉bind 127.0.0.1(61行) ,然后 protect-mode(80行)设置为 no
  • Jedis测试连通性

    import redis.clients.jedis.Jedis;
    public class TestJedis {
    	public static void main(String[] args) {
    		Jedis jedis = new Jedis("192.168.1.100", 6379);
    		String ping = jedis.ping();
    		System.out.println(ping);
    	}
    }
    

Redis的事务

* Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
* Redis事务的主要作用就是串联多个命令防止别的命令插队
  • Multi、Exec、discard

    * 从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,至到输入Exec后,Redis会将之前的命令队列中的命令依次执行。
    * 组队的过程中可以通过discard来放弃组队。  
    
  • 错误处理

    * 组队中某个命令出现了报告错误,执行时整个的所有队列会都会被取消。
    * 如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
    
  • * 乐观锁
    	WATCH balance
    * 悲观锁
    
  • 事务的特性

    * 事务的隔离操作
    	事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
    * 没有隔离级别的概念
    	队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在“事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题 
    * 不保证原子性
    	Redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚 
    

Redis的持久化

  • RDB(默认)

    * 在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
    
    • 备份执行

      * Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
      * RDB的缺点是最后一次持久化后的数据可能丢失。
      
    • fork

      在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”,一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
      
    • 文件保存

      * 配置文件中修改redis.conf
      	* 默认文件名dump.rdb
      	* 默认目录 ./	当前目录
      
    • 文件保存策略

      1573369483717

      • 手动保存

        命令save: 只管保存,其它不管,全部阻塞

        save vs bgsave:后台保存

    • 持久化策略

      * stop-writes-on-bgsave-error yes
      	当Redis无法写入磁盘的话,直接关掉Redis的写操作
      * rdbcompression yes
      	进行rdb保存时,将文件压缩
      * rdbchecksum yes
      	在存储快照后,还可以让Redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能
      
    • rdb备份和恢复

      备份*.rdb文件
      用备份文件恢复重启redis 
      
    • 优缺点

      * 优点
      	节省磁盘空间
      	恢复速度快
      * 缺点
      	虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。
      	在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
      
  • AOF

    * 以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
    	- 可以在redis.conf中配置文件名称,默认为 appendonly.aof
    
    * 默认不开启 appendonly yes开启
    
    * AOF和RDB同时开启
    	恢复按照AOF来
    
    * AOF文件备份和恢复
    	如遇到AOF文件损坏,可通过
         redis-check-aof  --fix  appendonly.aof   进行恢复
    
    * 同步策略
    	始终同步,每次Redis的写入都会立刻记入日志
    	每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
    	把不主动进行同步,把同步时机交给操作系统。
    
    * Rewrite
    	AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof。
    
    • 优点和缺点

      * 优点
          备份机制更稳健,丢失数据概率更低。
          可读的日志文本,通过操作AOF稳健,可以处理误操作。
      * 缺点
          比起RDB占用更多的磁盘空间。
          恢复备份速度要慢。
          每次读写都同步的话,有一定的性能压力。
          存在个别Bug,造成恢复不能。
      

Redis的主从复制

  • 概念

    * 主从复制,就是主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主
    
  • 用处

    1. 读写分离,性能扩展
    2. 容灾快速恢复
    
  • 配置

    * 配从不配主
    	- 拷贝多个redis.conf文件include
        - 开启daemonize yes
        - Pid文件名字pidfile
        - 指定端口port
        - Log文件名字
        - Dump.rdb名字dbfilename
        - Appendonly 关掉或者换名字
    
    
  • 一主二从模式

  • 薪火相传

  • 复制原理

    1. 每次从机联通后,都会给主机发送sync指令
    2. 主机立刻进行存盘操作,发送RDB文件,给从机
    3. 从机收到RDB文件后,进行全盘加载
    4. 之后每次主机的写操作,都会立刻发送给从机,从机执行相同的命令
    
  • 反客为主

    * slaveof  no one  
    
  • 哨兵模式

    * 反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库.
    * 配置
    	- 调整为一主二仆模式
    	- 自定义的/myredis目录下新建sentinel.conf文件
    	- 在配置文件中填写内容:
            sentinel  monitor  mymaster  127.0.0.1  6379  1
    	- 其中mymaster为监控对象起的服务器名称, 1 为 至少有多少个哨兵同意迁移的数量。
    * 启动哨兵
    	- 执行redis-sentinel  /myredis/sentinel.conf 
    
    * 故障恢复
    	-  从下线的主服务的所有从服务里面挑选一个从服务,将其转成主服务
    	选择条件依次为:
            1、选择优先级靠前的;优先级在redis.conf中slave-priority 100
            2、选择偏移量最大的;偏移量是指获得原主数据最多的
            3、选择runid最小的从服务;每个redis实例启动后都会随机生成一个40位的runid
       	- 挑选出新的主服务之后,sentinel 向原主服务的从服务发送 slaveof 新主服务 的命令,复制新master
    	- 当已下线的服务重新上线时,sentinel会向其发送slaveof命令,让其成为新主的从
    

Redis的集群

  • 集群概念

    * Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
    
    * Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
    * cluster nodes 命令查看集群信息
    
    
  • redis cluster分配节点

    * 一个集群至少要有三个主节点。
    * 选项 --replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。
    * 分配原则尽量保证每个主数据库运行在不同的IP地址,每个从库和主库不在一个IP地址上。
    
    
  • slots

    # 集群下登录redis客户端, -c 参数表示自动重定向
    redis-cli -c -p port
    # 会按照slot自动分给,并且跳转到对应的主机
    
    * slots描述
        一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
    
  • 在集群中插入值

    * 不在一个slot下的键值,是不能使用mget,mset等多键操作。
    * 可以通过{}来定义组的概念,从而使key中{}内相同内容的键值对放到一个slot中去。
    
  • 查询集群中的值

    * CLUSTER KEYSLOT key计算键 key 应该被放置在哪个槽上。
    * CLUSTER COUNTKEYSINSLOT slot 返回槽 slot 目前包含的键值对数量。  
    * CLUSTER GETKEYSINSLOT slot count 返回 count 个 slot 槽中的键。
    
  • 故障恢复

    • 如果主节点下线?从节点能否自动升为主节点?
    • 主节点恢复后,主从关系会如何?
    • 如果所有某一段插槽的主从节点都宕掉,redis服务是否还能继续?
    • redis.conf中的参数 cluster-require-full-coverage
  • 集群的Jedis开发

    public class JedisClusterTest {
      public static void main(String[] args) {
         Set<HostAndPort> set =new HashSet<HostAndPort>();
         set.add(new HostAndPort("192.168.1.100",6379));
         JedisCluster jedisCluster=new JedisCluster(set);
    
         jedisCluster.set("k1", "v1");
         System.out.println(jedisCluster.get("k1"));
      }
    }
    
  • Redis集群的好处和不足

    • 好处

      * 实现扩容
      * 分摊压力
      * 无中心配置相对简单
      
    • 不足

      * 多键操作是不被支持的 
      * 多键的Redis事务是不被支持的。lua脚本不被支持。
      * 由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片的方案想要迁移至redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。