Redis缓存入门

    xiaoxiao2025-06-07  69

    Redis缓存入门

    1.nosql介绍——Redis简介

    a.什么是NoSQL

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

    b.为什么需要NoSQL

    (1)High performance-对数据库高并发读写的需求

    (2)Huge Storage-对海量数据的高效率存储和访问的需求

    (3)High Scalability && High Availability-对数据库的高扩展性和高可用性的需求

    NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题

    c.主流的NoSQL产品

    mongoDB

    Redis

    d.NoSQL特点

    1.易扩展

    NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展,也无形之间,在架构层面上带来了可扩展的能力。

    2.大数据量,高性能

    NoSQL 数据库都具有非常高的读写性能,尤其是在大数据量下,同样表现优秀,这得益于它的无关系性,数据库结构简单。

    3.灵活的数据模型

    NoSQL 无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式,而在关系型数据库里,增删字段是一件非常麻烦的事情,如果是大数据量的表,增加字段简直就是一个噩梦。这点在大数量的Web2.0时代尤其明显。

    4.高可用

    NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构,比如Cassandra,HBase模型,通过复制模型也能实现高可用。

    e.NoSQL数据库的四大分类:

    键值存储数据库(key-value) 相关产品:Tokyo Cabinet/Tyrant,Redis,Voldemort,Berkeley DB典型应用:内容缓存,主要用于处理大量数据的高访问负载数据模型:一系列键值对优势:快速查询劣势:存储的数据缺少结构化 列存储数据库 相关产品:Cassandra,HBase,Riak典型应用:分布式的文件系统数据模型:以列簇式存储,将一列数据存在一起优势:查找速度快,可扩展性强,更容易进行分布式扩展劣势:功能相对局限 文档型数据库 相关产品:CouchDB,MongoDB典型应用:Web应用(与Key-Value类似,Value式结构化的)数据模型:一系列键值对优势:数据结构要求不严格劣势:查询性能不高,而且缺乏统一的查询语句 图形(Graph)数据库 相关数据库:Neo4J,InfoGrid,Infinite Graph典型应用:社交网络数据模型:图结构优势:利用图结构相关算法劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。

    f.什么是Redis

    ​ 由C语言实现的直接操作内存的开源的高性能的数据库软件

    ​ Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,他通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:

    字符串类型散列类型列表类型集合类型有序结合类型

    g.Redis的应用场景

    缓存(数据查询,短链接,新闻内容,商品内容)聊天室的在线好友列表任务队列(秒杀,帮抢,12306等)应用排行统计网站访问统计数据过期处理(可以精确到毫秒)分布式集群架构种的session分离

    2.安装Redis

    Redis安装包:以源码形式提供的 .c文件

    将.c文件编译为.o文件,需要安装:gcc

    将编译后的文件安装在Linux系统上

    a.安装gcc

    yum -y install gcc-c++

    b.获取安装包,上传到Linux服务器/root/

    c.解压安装包

    tar -zxvf redis-3.0.7.tar.gz

    d.编译,将源码翻译为.o文件

    make

    编译成功

    e.创建一个安装目录/usr/local/redis

    mkdir /usr/local/redis

    f.安装

    make PREFIX=/usr/local/redis install

    安装成功

    安装成功后,在/usr/local/redis/bin下有几个可执行文件

    redis-benchmark ---性能测试工具 redis-check-aof ---AOF文件修复工具 redis-check-dump ---RDB文件检查工具(快照持久化文件) redis-cli ---命令行客户端 redis-server ---redis服务器启动命令

    g.复制源码中的配置文件到安装目录中去

    cp redis.conf /usr/local/redis

    h.redis的启动

    前端模式启动

    /usr/local/redis/bin/redis-server

    后台模式启动

    1.修改配置文件

    vim /usr/local/redis/redis.conf

    启动时,指定配置文件

    cd /usr/local/redis/ ./bin/redis-server ./redis.conf

    redis默认端口6379,通过当前服务进行查看

    ps -ef | grep -i redis

    centos7开放redis端口

    firewall-cmd --permanent --zone=public --add-port=6379/tcp

    启动redis客户端

    ./bin/redis-cli

    i.连接redis客户端

    redis-cli -h ip地址 -p 端口 -a 密码

    j.Redis配置文件详解

    Redis的配置文件位于Redis安装目录下,名为redis.conf(Window名为redis.windows.conf)

    //1.redis默认不是以守护进程的方式运行的,可以通过配置修改,使用yes启用守护进程 daemonize no //2.当redis以守护进程运行时,redis默认把pid写入/var/run/redis.pid文件,可以通过pidfile指定 pidfile /var/run/redis.pid //3.指定redis监听端口,默认端口为6379 port 6379 //4.绑定主机地址 bind 127.0.0.1 //5.当客户机闲置多长时间后关闭连接,如果指定为0,表示关闭该功能 timeout 0 //6.指定日志记录级别,redis总共支持4个级别:debug,verbose,notice,warning,默认为verbose loglevel verbose //7.设置数据库数量,默认数据库为0,可以使用select <dbid>命令连接上指定数据库id,默认有0-15总共16个数据库 databases 16 //8.指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合 save <秒数> <更新数> //9.指定存储到本地数据库是否压缩数据,默认为yes,redis采用LZF压缩,如果为了节省cpu时间,可以关闭该选项,但会导致数据库文件变得巨大 rdbcompression yes //10.指定本地数据库文件名,默认值为dump.rdb dbfilename dump.rdb //11.指定本地数据库存放目录 dir ./ //12.设置redis连接密码,如果配置了连接密码,客户端在连接redis时需要通过AUTH <password>命令提供密码,默认关闭。 requirepass foobared //13.设置最大客户端连接数 maxclients 1000 //14.设置redis最大内存限制,建议: maxmemory <bytes> //15.指定是否在每次更新后进行日志记录,redis在默认情况下时异步的把数据写入硬盘,如果不开启,可能会在断电时导致一段时间内的数据丢失,因为redis本身同步数据文件是上面的save条件来同步的,所以有的数据会在一段时间内只存在于内存中,默认为no。 appendonly no //16.指定更新日志文件名,默认为appendonly.aof appendfilename appendonly.aof redis作为优秀的缓存中间件,时常会存储大量的数据,即使采用了集群部署来动态扩容,也应该即时的整理内存,维持系统性能(如果数据一直新增,内存很快就会沾满) 在redis中有两种解决方案: 1.为数据设置超时时间,设定内存空间,建议内存不要超过1G 256-512m 2.采用LRU算法动态将不用的数据删除。内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据那些数据属于LRU而将其移出内存而腾出空间来加载另外的数据。 (1)volatile-lru:设置超时时间的数据中,删除最不常用的数据. (2)allkeys-lru:查询所有的key中最近不常使用的数据进行删除,这是应用最广泛的策略。 (3)volatile-random:在已经设定了超时的数据中随机删除。 (4)allkeys-random:查询所有key之后随机删除。 (5)volatile-ttl:查询所有设定超时时间的数据之后,将马上要过期的数据进行删除操作。 (6)Noeviction:如果设置为该属性,则不会进行删除操作,如果内存溢出,则报错返回。 (7)volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键。 (8)allkeys-lfu:从所有键中驱逐使用频率最少的键。

    k.redis关闭

    第一种关闭方式:(断电,非正常关闭。容易数据丢失)

    查询PID ps -ef | grep -i redis

    kill -9 PID

    第二种关闭方式(正常关闭,数据保存)

    ./bin/redis-cli shutdown

    3.Redis的数据结构

    a. 使用Redis命令操作常用数据结构的的数据存取

    key的操作 在Redis里边,除了"\n"和空格,不能作为名字的组成内容外,其他内容都可以作为key的名字部分。名字长度不做要求。 redis支持的数据类型 keys 给存储在redis内存中的数据起的变量名字 Values Strings(Binary-safe strings)Lists(Lists of binary-safe strings)Sets(Sets of binary-safe strings)Sorts sets(Sorts sets of binary-safe strings)Hash keys键操作 exists key 测试指定key是否存在del key1 key2 … keyN 删除指定的keytype key 返回给定key的value类型keys pattern 返回匹配指定模式的所有key keys * * 代表所有,?代表一个字符rename oldkey newkey 改名字dbsize 返回当前数据库key数量expire key seconds 为key指定过期时间ttl key 返回key剩余的过期秒数select db-index 选择数据库move key db-index 将key从当前数据库移动到指定数据库flushdb 删除当前数据库中所有keyflushall 删除所有数据库中的所有的keydump key 序列化指定key,并返回序列化的值pttl key 以毫秒为单位返回key的剩余的过期时间persist key 移除key的过期时间,key将持久保存

    key名称不区分大小写

    b.String

    简介

    String 是redis的最基本的数据类型,一个key对应一个value。

    String类型是二进制安全的,意思是redis的String可以包含任何数据。比如jpg图片或者序列化的对象。

    String类型是redis最基本的数据类型,一个键最大能存储512Mb

    二进制安全是指:在传输数据时,保证二进制数据的信息安全,也就是不被篡改,破译等,如果被攻击,能够及时检测出来。

    二进制安全的特点:

    1.编码,解码发生在客户端完成,执行效率高

    2.不需要频繁的编解码,不会出现乱码。

    String命令

    //1.赋值语法 set key_name value //SET 命令用于设定给定的key的值,若key已经存值,set就覆盖旧值,且无视类型 setnx key value //只有在key不存在时设置key的值。setnx(Set if Not eXists)命令在指定的key不存在时,为key设置值。 MSET key value [key value...] //同时设置一个或多个key-value对 //2.取值语法 GET KEY_NAME //GET命令用于获取指定key的值。如果key不存在,就返回nil。如果key存储的不是字符串类型,就报错。 GETRANGE key start end //用于获取存储在指定key中字符串的子串,字符串的截取范围有start和end两个偏移量决定(包括start和end)。 MGET key1 [key2...] //获取所有(一个或多个)指定key的值 GETSET key_name value //GETSET命令用于设置指定key的值,并返回key的旧值,当key不存在时,返回nil。 STRLEN key //返回key所储存的字符串值的长度。 //3.删除语法 DEL key_name //删除指定的key,如果存在,返回值数字类型 //4.自增|自减 INCR key_name //INCR命令将key存储的数字值增1,如果key不存在,那么key的值会先初始化为0,然后再执行incr操作。 INCRBY key_name 增量 //incrby命令将key中存储的数字加上指定增量 DECR key_name或DECRBY key_name 减量 //decr命令将key中所存储的数字减1 //5.字符串拼接 APPEND key_name value //append命令用于为指定的key追加至末尾,如果不存在,为其赋值。

    String类型的应用场景

    1.String类型通常用于保存单个字符串或JSON字符串数据

    2.因为String是二进制安全的,所以你完全可以把一个图片文案的内容作为字符串来存储。

    3.计数器

    c.哈希(Hash)

    redis中的Hash类型类似于java中的一个JavaBean

    简介

    Redis Hash是一个String类型的filed和value的映射表,hash特别适合用于存储对象,Redis中每个hash可以存储2^32-1键值对(40多亿)。可以看成具有Key和Value的MAP类型,该类型非常适合于存储键值对象的信息,改类型的数据仅占用很少的内存空间(相比于JSON)

    HASH命令

    //1.赋值语法 HSET key FILED value //未指定的key,设定FILED/Value HMSET KEY FILED VALUE [FILED1,VALUE1]... //同时将多个filed-value对设置到hash表key中 //2.取值语法 HGET KEY FILED //获取存储在HASH中的值,根据FIELD得到value HMGET KEY field [field1] //获取key所有给定字段的值 HGETALL key //返回HASH表中所有字段和值 HKEYS key //获取所有哈希表中的字段 HLEN key //获取哈希表中字段的数量 //3.删除语法 HDEL KEY field[field1] //删除一个或多个HASH表字段 //4.其他语法 HSETNX key field value //只有在字段不存在时,设置hash表字段值 HINCRBY key field increment //为哈希表key中指定字段的整数加上增量increment HINCRBYFLOAT key field increment //为哈希表key中指定字段的浮点数加上增量increment HEXISTS key field //查看哈希表key中,指定的字段是否存在

    应用场景:(用于存储一个用户信息对象数据)

    redis的HASH实际上是内部存储的value为一个HashMap,并提供了直接存取这个Map成员的接口。

    4.在Java程序中通过Jedis连接Redis

    1.新建项目,添加redis的依赖jar包:jedis-3.0.0.jar

    2.使用jedis连接redis服务器

    Jedis jedis = new Jedis("192.168.159.128",6379);

    3.redis服务器有密码则还需验证密码

    jedis.auth("password");

    4.测试是否连接成功

    //测试redis服务器是否连接成功,连接成功则会在控制台输出PONG System.out.println(jedis.ping());

    5.redis存取字符串类型测试

    /** * 字符串类型的存取 */ @Test public void demo02(){ Jedis jedis = new Jedis("192.168.159.128",6379); jedis.append("strName", "胡卫平"); System.out.println(jedis.get("strName")); jedis.del("name:1"); System.out.println(jedis.get("name:1")); jedis.close(); }

    6.redis作用的使用

    /** * Redis作用:为了减轻数据库的访问压力 * 需求:判断某key是否存在,若存在,就从redis中查询 * 若不存在,就查询数据库,并且将数据库查询出的数据存入Redis中 */ @Test public void demo03(){ Jedis jedis = new Jedis("192.168.159.128",6379); String key = "uname:1"; if(jedis.exists(key)){ System.out.println("Redis中查询得到:"+key+"="+jedis.get(key)); }else{ QueryRunner runner = new QueryRunner(JDBCUtil.dataSource); try { String query = (String)runner.query("select uname from account where id=?", new ScalarHandler(), key.split(":")[1]); System.out.println("Mysql中查询得到:"+key+"="+query); jedis.set(key, query); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } jedis.close(); } }

    7.Jedis连接池优化

    添加连接池的依赖包:commons-pool2-2.6.1.jar,以及相应的log4j的jar包 - /** * Jedis连接池优化 */ @Test public void demo04(){ //1.创建连接池Redis Pool //配置连接池的基本xinxi JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(5); //最大连接数 config.setMaxIdle(1); //最大空闲数 //2.创建连接池 JedisPool pool = new JedisPool(config, "192.168.159.128",6379); //3.获取连接对象 Jedis jedis = pool.getResource(); System.out.println(jedis.get("uname:2")); }

    8.创建连接池工具类

    package com.nike.demo01; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisPoolUtil { private static JedisPool pool; static{ //1.创建连接池Redis Pool //配置连接池的基本xinxi JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(5); //最大连接数 config.setMaxIdle(1); //最大空闲数 //...其他配置 //2.创建连接池 pool = new JedisPool(config, "192.168.159.128",6379); } public static Jedis getJedis(){ return pool.getResource(); } //关闭redis连接 public static void close(Jedis jedis){ jedis.close(); } }

    9.测试工具类

    @Test public void demo05(){ Jedis jedis = JedisPoolUtil.getJedis(); System.out.println(jedis.get("uname:7")); JedisPoolUtil.close(jedis); }

    10.Jedis读取Hash类型数据操作

    /** * 使用hash类型模拟对象的存储 * 需求:1.判断redis中是否存在改key,如果存在,直接返回对应值; * 2.如果不存在,查询数据库,将数据库查询到的结果存储到数据库中,并返回给用户。 */ @Test public void demo06(){ Jedis jedis = JedisPoolUtil.getJedis(); String key = "account:7"; if(jedis.exists(key)){ Map<String, String> map = jedis.hgetAll(key); Set<Entry<String, String>> entrySet = map.entrySet(); System.out.println("从Redis数据库查询得到的结果:"); for (Entry<String, String> entry : entrySet) { System.out.println(entry.getKey()+":"+entry.getValue()); } }else{ QueryRunner runner = new QueryRunner(JDBCUtil.getDataSource()); String sql = "select * from account where id = ?"; try { Map<String, Object> query = runner.query(sql, new MapHandler(), key.split(":")[1]); Set<Entry<String, Object>> entrySet = query.entrySet(); System.out.println("从MySql数据库查询得到的结果:"); for (Entry<String, Object> entry : entrySet) { jedis.hset(key, entry.getKey(), entry.getValue().toString()); System.out.println(entry.getKey()+":"+entry.getValue().toString()); } } catch (SQLException e) { e.printStackTrace(); } } JedisPoolUtil.close(jedis); }

    将Redis中查询的结果通过BeanUtil封装成一个java对象

    @Test public void demo07(){ Jedis jedis = JedisPoolUtil.getJedis(); String key = "account:7"; Map<String, String> map = jedis.hgetAll(key); Account account = new Account(); try { BeanUtils.populate(account, map); System.out.println(account); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } JedisPoolUtil.close(jedis); } }

    4.Spring整合Redis

    Spring data 提供了 RedisTemplate 模板

    它封装了 redis 连接池管理的逻辑,业务代码无需关心获取,释放连接逻辑;

    Spring redis 同时支持了Jedis,Jredis,rjc客户端操作;

    在 RedisTemplate 中提供了几个常用的接口方法的使用,分别是

    //cache singleton objects (where possible: private ValueOperations<K,V> valueOps; private ListOperations<K,V> listOps; private SetOperation<K,V> setOps; private ZSetOperation<K,V> zSetOps; @Resourse(name="redisTemplate") ListOperations<String,String> list;

    1.导入相关依赖的jar包

    目前还搞不定

    2.对应实体Bean进行序列化操作

    package com.nike.spring.domain; import java.io.Serializable; public class Account implements Serializable{private static final long serialVersionUID = 1L; private int id; private String uname; private String upwd; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } public String getUpwd() { return upwd; } public void setUpwd(String upwd) { this.upwd = upwd; } @Override public String toString() { return "Account [id=" + id + ", uname=" + uname + ", upwd=" + upwd + "]"; } }

    3.编写相应配置文件spring_redis.xml,利用RedisTemplate操作Redis

    <?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"> <!-- 1.配置连接池配置信息 --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 最大连接数 --> <property name="maxTotal" value="100"/> <!-- 最大空闲数 --> <property name="maxIdle" value="20"/> <!-- 最大建立连接等待时间 --> <property name="maxWaitMillis" value="2000"/> <!-- 指明是否从池中取出连接连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 --> <property name="testOnBorrow" value="true"/> </bean> <!-- 2.配置连接工厂 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="usePool" value="true"/> <property name="hostName" value="192.168.159.128"/> <property name="port" value="6379"/> <constructor-arg index="0" ref="jedisPoolConfig"/> </bean> <!-- redisTemplate配置,redisTemplate是对redis操作的扩展,有更多的操作,封装使操作更便捷 --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory"/> </bean> </beans>

    4.编写测试类

    package com.nike.spring.test; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * spring整合redis的测试类 * @author 猪猪 * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring_redis.xml") public class SpringTest01 { @Resource(name="redisTemplate") private RedisTemplate<String, String> redisTemplate; @Test public void demo01(){ ValueOperations<String, String> value = redisTemplate.opsForValue(); if(redisTemplate.hasKey("uname:1")){ System.out.println(value.get("uname:1")); } } }

    5.Redis其他数据结构操作

    list:列表类型

    简介

    类似于Java语言中的LinkedList类型

    Redis列表是简单的字符串列表,按照插入的顺序排序,你可以添加一个元素到列表的头部或尾部,一个列表最多可以包含2^32-1个元素。

    命令

    //1.赋值命令 LPUSH key value1[value2] //将一个或多个值插入到列表头部 RPUSH key value1[value2] //将一个或多个值插入到列表的尾部 LPUSHX key value //将一个值插入到已存在列表的头部,若列表不存在,操作无效 RPUSHX key value //将一个值插入到已存在列表的尾部,若列表不存在,操作无效 //2.取值命令 LLEN key //获取列表长度 LINDEX key index //通过索引获取列表中的元素,索引左侧从0开始,右侧从-1开始 LRANGE key start stop //获取列表指定范围内的元素 //3.删除语法 LPOP key //移除并获取列表的第一个元素(从左侧移除) RPOP key //移除并获取列表的最后一个元素(从右侧移除) BLPOP key1 [key2] timeout //移除并获取列表的第一个元素,如果列表没有元素直到等待超时或发现可弹出元素为止 BRPOP key1 [key2] timeout //移除并获取列表的最后一个元素,如果列表没有元素直到等待超时或发现可弹出元素为止 LTRIM key start stop //对一个列表进行修剪,让列表只保留指定区域内的元素,不在指定区域内的元素都将被删除 //4.修改语法 LSET key index value //通过索引设置元素的值 LINSERT key BEFORE|AFTER world value //在列表的元素前或后插入元素

    应用场景

    项目常用于:

    1.对数据量大的集合数据删减

    列表数据显示,关注列表,粉丝列表,留言评价等…分页,热点新闻等利用LRANGE还可以很方便的实现分页功能,在博客系统中,每页的博文评论也可以存入一个单独的list中

    2.任务队列

    list通常用来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样通过order by进行排序介绍:在处理web客户端发送的命令请求时,某些操作的执行时间可能会比我们预期的长一些,通过将执行任务的相关信息放入队列里面,并在之后对队列进行处理,用户可以推迟执行那些需要一段时间才能完成的工作,这种将工作交给任务处理器来执行的做法被称为任务队列(task quene)。

    高级命令

    RPOPLPUSH source destination //移除源列表的最后一个元素,并将该元素添加到目标列表的头部并返回,源列表和目标列表可以是同一个列表,可以实现队列,常用案例:订单系统的下单流程,用户系统登陆注册短信等。 BRPOPLPUSH source destination timeout //移除源列表的最后一个元素,并将该元素添加到目标列表的头部并返回;如果源列表中没有元素,会阻塞列表直到等待超时或发现可弹出元素为止。

    Set:集合类型

    简介

    Redis的Set使String类型的无序集合,集合的成员使唯一的,这意味着集合中不能出现重复的数据。

    Redis中的集合使通过哈希表实现的,所以添加,删除,查找的难度都是o(1),集合中最大的成员数为2^32-1,类似于Java中的Hashtable集合。

    redis的集合对象 set 的底层数据结构特别神奇,底层采用了intset和hashtable两种数据结构存储的,intset可以理解为数组,hashtable就是普通哈希表(key为set的值,value为null)。intset内部是一个数组(int8_t coentents[]数组),而且存储数据的时候是有序的,应为在查找数据的时候是通过二分查找来实现的。

    命令

    //1.赋值语句 SADD key member1 [member2] //向集合添加一个或多个成员 //2.取值语法 SCARD key //获取集合成员数 SMEMBERS key //返回集合中所有的成员 SISMEMBER key member //判断member元素是否在集合key的成员 SRANDMEMBER key [count] //返回集合中一个或多个随机数 //3.删除语法 SREM key member1 [member2] //移除集合中一个或多个元素 SPOP key [count] //移除并返回一个随机元素 SMOVE source destination member //将member元素从源集合移动到目标集合 //4.差集语法 SDIFF key1 [key2] 返回给定所有集合的差集(左侧) SDIFFSTORE destination key1[key2] //返回所有集合的差集并存储到目标集合中 //5.交集语法 SINTER key1 [key2] //返回给定所有集合的交集 SINTERSTORE dest key1 [key2] //返回给定所有集合的交集并存储在dest中 //6.并集语法 SUNION key1 [key2] //返回所有给定集合并集 SUNIONSTORE dest key1 [key2] //返回所有给定的集合的并集并存储在dest集合中

    应用场景

    常应用于:对两个集合间的数据进行交集,并集,差集运算。

    1.可以非常方便的实现如同共同关注,共同喜好,二度好友等功能,

    2.利用唯一性,可以统计访问网站的所有独立ip

    ZSet:有序结合(Sorted Set)

    简介

    1.Redis有序集合和集合是一样的也是String类型元素的集合,且不允许重复的成员;

    2.不同的是每个元素关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大排序;

    3.有序集合的成员是唯一的,但分数却可以重复;

    4.集合是通过哈希表实现的,所以添加,删除,查找的复杂难度都是o(1)。集合中的最大成员数为2^32-1.

    命令

    //1.赋值语法 ZADD key score1 member1 [score2 member2] //向有序集合中添加一个或多个成员,或者更新已存在的成员的分数 //2.取值语法 ZCARD key //获取有序集合的成员数 ZCOUNT key min max //计算在有序集合中指定区间分数的成员数 ZBANK key member //返回有序集合中指定成员的索引 ZRANGE key start stop [withscores] //通过索引区间返回有序集合指定区间内的成员(低到高) ZREVRANGE key start stop [withscores] //通过索引区间返回有序集合指定区间内的成员(高到低) //3.删除语法 DEL key //移除集合 ZREM key member1 [member2] //移除有序集合中一个或多个成员 ZREMRANGEBYRANK key start stop //移除有序集合中给定的排名区间的所有成员(第一名是0) ZREMRANGEBYSCORE key min max //移除有序集合中给定分数区间的所有成员

    应用场景

    常应用于:排行榜

    1.比如twitter的public timeline 可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。

    2.比如一个存储全班成绩的Sorted set,其集合value可以学生的学号,而score就可以是其考试得分,这样在插入数据时,就已经进行了天然的排序。

    3.还可以用Sorted Set来做带权重的任务队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务,让重要的任务优先执行。

    6.Redis特性——消息订阅和发布,事务支持

    Redis 发布订阅

    简介

    Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收信息。

    Redis客户端可以订阅任意数量的频道

    命令

    //1.订阅频道 SUBSCRIBE channel [channel ...] //订阅给定的一个或多个频道 PSUBSCRIBE pattern [pattern ...] //订阅一个或多个符合给定模式的频道 //2.发布频道 PUBLISH channel message //将消息发送到指定的频道 //3.退订频道 UNSUBSCRIBE channel [channel ...] //退订给定的频道 PUNSUBSCRIBE pattern [pattern ...] //退订所有给定模式的频道

    应用场景

    构建实时消息系统,比如普通的即时聊天,群聊等功能

    1.在一个博客网站中,有100个粉丝订阅了你,当你发布新文章时,就可以推送消息给粉丝们。

    2.微信公众号模式

    Redis 多数据库

    Redis下,数据库是由一个整数索引标识,而不是一个数据名称。默认情况下,一个客户端连接到的数据库0.

    redis配置文件中下面的参数来控制数据库总数

    database 16 //(从0开始)

    命令

    select db //数据库切换 move key db //将指定的key移动到指定的数据库 flushdb //清空当前数据库所有的key flushall //清除整个redis的数据库所有的key

    Redis 事务

    简介

    Redis可以一次执行多个命令(按顺序串行化执行,执行中不会被其他命令插入,不许加塞,允许在一次单独的步骤中执行一组命令),并且带有一下两个重要保证:

    批量操作在发送EXEC命令前被放入队列缓存。

    收到EXEC命令后进入事务执行,事务中任意命令执行失败,其余命令依然执行。

    在事务执行过程中,其他客户端提交的命令不会插入到事务执行命令序列中。

    1.Redis会将一个事务中所有命令序列化,然后按顺序执行;

    2.执行中不会被其他命令插入,不允许出现加塞行为

    一个事务从开始到执行会经历以下3个阶段:

    开始事务

    命令入队

    执行事务

    一个事务的例子,它先以MULTI开始一个事务,然后将多个命令入队到事务中,最后由EXEC命令触发事务

    A向B转账50元

    1.输入MUTLI命令开始,输入的命令都会依次进入命令队列中,但不会执行;

    2.直到输入EXEC后,redis会将之前队列中的命令依次执行。

    3.输入命令队列的过程中可以通过DISCARD来放弃队列执行

    Redis事务命令

    DISCARD //取消事务,放弃执行所有事务 EXEC //执行所有事务块内的命令 MULTI //标记一个事务块的开始 UNWATCH //取消WATCH命令对所有key的监视 WATCH key [key ...] //监视一个或多个key,如果在事务执行之前这个(或这些个)key被其他命令所改动,那么事务将被打断。

    事务的错误处理

    如果执行的某个命令报出了错误(业务错误),则只有报错的命令不会执行,而其他命令都会执行,不会回滚。

    队列中的某个命令出现了报告错误(命令语法上的错误),执行时整个的所有队列都会被取消

    如果在执行了WATCH命令之后,EXEC或DISCARD命令先被执行的话,那就不需要执行UNWATCH了

    应用场景

    一组命令必须同时都执行,或都不执行。

    我们想要保证一组命令执行过程中不被其他命令插入

    商品秒杀

    Redis数据淘汰策略

    Redis官方给出的警告,当内存不足时,Redis会根据配置的缓存策略淘汰部分keys,以保证写入成功。当无淘汰策略时或没有找到适合淘汰的key时,Redis直接返回out of memory错误

    Redis持久化

    简介

    数据存放于内存中

    内存:高效,断电(关机)内存数据会丢失

    硬盘:读写速度慢于内存,断电数据不会丢失

    RDB

    RDB:是Redis的默认持久化机制,RDB相当于照快照,保存的是一种状态。几十G的数据---->几kb快照

    快照是默认的持久化方式,这种方式是将内存中数据以快照的方式写入到二进制文件中,默认文件名为dump.rdb

    优点:快照保存数据极快,还原数据极快,适用于灾难备份。

    缺点:小内存机器不适合使用,RDB机制符合要求就会照快照。

    快照条件:

    AOF

    由于快照方式是在一定时间间隔做一次,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改,如果应用要求不能丢失任何修改的话,可以采用aof持久化方式。

    Append-only file:aof 比快照有更好的持久性,是由于使用aof时,redis会将每一个收到的命令都通过write函数追加到文件中(appendonly.file)。当redis重启时会通过重新执行文件中保存的命令来在内存中重建整个数据库的内容。

    有以下三种方式(默认:每秒fsync一次):

    appendonly yes //启用aof持久化方式#appendfsync always //收到写命令就立即写入磁盘,最慢,但是保证完全的持久化appendfsync everysec //每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中#appendfsync no //完全依赖 os ,性能最好,持久化没有保证

    产生的问题:

    aof的方式也同时带来了一个问题。持久化文件会变得越来越大.

    7.Redis 缓存和数据库一致性

    解决方案

    一.实时同步

    对强一致性要求比较高的,应采用实时同步方案,即查询缓存查询不到再从DB查询,保存到缓存;更新缓存时,先更新数据库,再将缓存的设置过期(建议不要更新缓存内容,直接设置缓存过期)。

    @Cacheable:查询时使用,注意Long类型需转换成String类型,否则会抛异常;

    @CachePut:更新时使用,使用此注解,一定会从DB上查询数据

    @CacheEvict:删除时使用

    @Caching:组合使用法

    缓存穿透

    缓存穿透是指查询一个一定不存在的数据,由于缓存不命中时需要查询数据库,查不到数据则不写入缓存,这将导致这个不存在的数据每次查询都要到数据库去查询,造成缓存穿透。

    解决办法:持久层查询不到就缓存空结果,查询时先判断缓存中是否exists(key),如果有直接返回空,没有则查询后返回,注意insert时需清空查询的key,否则即便DB中有值也查询不到(当然也可以设置空缓存的过期时间)。

    二.异步队列

    对于并发程度较高的,可采用异步队列的方式同步,可采用kafka等消息中间件处理消息生产和消费

    三.使用阿里的同步工具canal

    canal实现方式是模拟mysql slave和master的同步机制,监控DB bitlog的日志更新来触发缓存的更新,此种方法可以释放程序员的双手,减少工作量,但在使用时有些局限性

    MySql主从复制实现
    master将改变记录到二进制文件日志中(binary log)中(这些记录叫做二进制日志文件binary log events);可以通过show binlog events进行查看;slave将master的binary log events拷贝到它的中继日志(relay log);slave重做中继日志中的事件,将改变反映它自己的数据。

    四.采用UDF自定义函数的方式

    面对mysql的API进行编程,利用触发器惊醒缓存同步,但UDF主要是C/C++语言实现,学习成本高。

    雪崩

    雪崩:缓存大量失效的时候,引发大量查询数据库。

    解决办法:1. 用锁/分布式或队列串行访问;

    2.缓存失效时间均匀分布。

    8.Redis高级

    1.可能遇见的问题

    一般来说,要将redis运用于工程项目中,只使用一台Redis是万万不能的,原因如下:

    从结构上,单个Redis服务器会触发单点故障,并且一台服务器需要处理所有请求负载,压力较大;从容量上,单个Redis服务器内部容量有限,就算一台Redis服务器内容量为256G,也不应该将所有内容用作Redis存储内存,一般来说,单台Redis最大使用内存不应该超过20G。

    2.基本概述

    高可用:高可用性(High Availability)通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性高并发:高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常指,通过设计保证系统能够同时并行处理很多请求。 高并发相关常用的一些指标有响应时间(Response Time),吞吐量(Throughtput),每秒查询率(QPS,Query Per Second),并发用户数等响应时间:系统对请求做出响应的时间吞吐量:单位时间内处理的请求数量QPS:每秒响应请求数并发用户数:同时承载正常使用系统功能的用户数。提高系统的并发能力:垂直扩展和水平扩展

    3.Redis主从复制

    简介

    应用场景:电子商务网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是"多读少写"。

    主从复制:

    一个Redis服务可以有多个该服务的复制品,这个Redis服务称为Master,其他复制称为Slaves,Master主要负责写请求,Slaves负责读请求。

    主库只负责写请求,每次有数据更新都将更新的数据同步到它所有的从库,而从库只负责读数据,这样一来,就有了两个好处:

    读写分离,不仅可以提高服务器的负载能力,并且可以根据读请求的规模自由增加或减少从库的数量。数据被复制成了好几份,就算有一台机器出现了故障,也可以使用其他机器的数据快速恢复。

    Redis主从复制配置

    在Redis中,要实现主从复制架构十分简单,只需要在数据库配置文件中加上如下命令即可:

    --port 6378 //从服务器的端口 --slaveof 127.0.0.1 6379 //指定 主服务器 ./bin/redis-server ./redis.conf -port 6378 -slaveof 127.0.0.1 6379 //加上slaveof参数启动另一个Redis实例作为从库 并且监听6378端口

    登陆到从服务器

    ./bin/redis-cli -p 6380 slaveof on one //变回主 slaveof IP port //变回从

    4.RedisCluster集群概述

    简介

    为什么使用集群为了在大流量访问下提供稳定的业务,集群化是存储的必然形态未来的发展趋势坑定是云计算和大数据的紧密结合只有分布式架构能满足要求
    Redis集群搭建方案

    (1)Twitter开发的twemproxy

    (2)豌豆荚开发的codis

    (3)redis官方开发的redis-cluster

    Redis集群搭建至少需要3(Master)+ 3(slave)才能建立集群,采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。

    Redis集群特点

    1.所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。

    2.节点的fail是通过集群中超过集群中超半数的节点检测失效时才生效

    3.客户端于redis节点直连,不需要中间proxy,客户端不需要连接集群所有节点,连接集群中任何一个节点即可。

    4.redis-cluster中把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster负责维护

    5.redis集群预分好16384个哈希槽,当需要在Redis 集群中放置一个key-value时,redis预先对key使用crc16算法算出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽,redis会根据节点数量大致均等的将哈希槽映射到不同的节点。

    Redis Cluster容错

    容错性:是指软件检测应用程序所运行的软件或硬件中发生错误并从错误中恢复的能力,通常可以从系统的可靠性,可用性,可测性等几个方面来衡量

    redis-cluster投票:容错

    1.投票过程是集群中所有master参与,如果半数以上master节点于master节点通信超时(cluster-note-timeout),认为当前节点挂掉

    2.什么时候整个节点不可用(cluster_state:fail)

    如果集群任意master挂掉,且当前master没有slave,集群进入fail状态,也可以理解为集群的slot映射[0-16383]不完整是进入fail状态,如果集群超过半数以上master挂掉,无论是否有slave,集群进入fail状态

    Redis-cluster节点分配

    (官方推荐)三个主节点分别是A,B,C三个节点,他们可以是一台机器上的三个端口,也可以是三台不同的服务器,那么,采用哈希槽的方式来分配16384个slot的话,它们三个节点分别承担的slot区间是

    节点A覆盖:0-5460;

    节点B覆盖:5461-10922

    节点C覆盖:01923-16383

    新增一个节点:redis cluster的这种做法是从各个节点的前面各拿取一部分slot到D上

    节点D覆盖:0-1364,5461-6826,10923-12287

    5.Redis集群搭建

    最少需要6台机器,才能完成Redis Cluster集群

    搭建流程

    1.创建Redis节点安装目录

    mkdir /usr/local/redis_cluster //指定目录下,创建redis_cluster

    2.在redis_cluster目录下,创建7001-7006个文件夹

    mkdir 7001 7002 7003 7004 7005 7006

    3.并将redis.conf分别拷贝到7001-7006文件夹下

    cp redis/redis.conf ./7001

    4.分别修改如下配置文件,修改内容如下:

    同时protected-mode是为了禁止公网访问redis cache,加强了redis安全,它启用的条件有两个:

    1.没有bind ip

    2.没有设置访问密码

    5.启动各个redis节点

    将redis/src文件拷贝到各个redis 7001-7006目录下

    启动各个节点

    cd redis_cluster/ ./7001/src/redis-server ./7001/redis.conf //依次启动

    检测redis节点启动状态

    ps -ef | grep redis

    6.创建集群

    Redis官方提供了redis-trib.rb这个工具,就在解压目录的src目录中,(为了方便你操作)将其复制到/usr/local/bin目录下。可直接访问此命令(需要安装ruby)

    redis-trib.rb create --replicas 1 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 127.0.0.1:7006

    安装ruby

    yum -y install ruby ruby-devel rubygems rpm-build gem install redis

    安装rvm,升级ruby

    gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB curl -sSL https://get.rvm.io | bash -s stable source /etc/profile.d/rvm.sh rvm -v rvm list known rvm install 2.5

    创建集群

    gem install redis redis-trib.rb create --replicas 1 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 127.0.0.1:7006

    访问集群

    redis-cli -h 127.0.0.1 -c -p 7001

    查看节点信息

    查看所有节点信息

    最新回复(0)