Redis入门

Redis

  • 概念: redis是一款高性能的NOSQL系列的非关系型数据库
    • 什么是NOSQL

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

  • NOSQL和关系型数据库比较

  1. 优点:
  • 成本:nosql数据库简单易部署,基本都是开源软件,不需要像使用oracle那样花费大量成本购买使用,相比关系型数据库价格便宜。
  • 查询速度:nosql数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询速度远不及nosql数据库。
  • 存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型。
  • 扩展性:关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难。
  1. 缺点:
  • 维护的工具和资料有限,因为nosql是属于新的技术,不能和关系型数据库10几年的技术同日而语。
  • 不提供对sql的支持,如果不支持sql这样的工业标准,将产生一定用户的学习和使用成本。
  • 不提供关系型数据库对事务的处理。

  • 非关系型数据库的优势:
  1. 性能NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解析,所以性能非常高。
  2. 可扩展性同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。
  • 关系型数据库的优势:
  1. 复杂查询可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。
  2. 事务支持使得对于安全性能很高的数据访问要求得以实现。对于这两类数据库,对方的优势就是自己的弱势,反之亦然。

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

  • 主流的NOSQL产品

redis、memcached、mongodb、guava(loadingCache)


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

redis和memcached各有什么优势

  • 内存管理机制
    • Memcached默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块 以存储相应长度的key-value数据记录,以完全解决内存碎片问题。空闲列表进行判断存储状态
    • Redis使用现场申请内存的方式来存储数据,并且很少使用free-list(空闲列表)等方式来优化内存分配,会在一定程度上存在内存碎片【CPU内存是连续,类似于Java虚拟机对象的分配,直接内存分配(指针碰撞)】
memcached空闲分配.jpg
memcache数据存储.jpg

如上,memcached预先定义很多内存大小的空间,并记录,然后根据存储数据的大小来选择那种大小的空间,而redis则是直接存储,即使120一段,实际存储的数据只占用20,那么就浪费了100的内存。

redis数据存储.png
  • 数据持久化方案

    • memcached不支持内存数据的持久化操作,所有的数据都以in-memory的形式存储。
    • redis支持持久化操作。redis提供了两种不同的持久化方法来讲数据存储到硬盘里面,
      • 第一种是rdb形式,一种是aof形式
        • rdb:属于全量数据备份,备份的是数据
        • aof:append only if,增量持久化备份,备份的是指令[如:set key, del key]
  • 缓存数据过期机制

    • Memcached 在删除失效主键时也是采用的消极方法,即 Memcached 内部也不会监视主键是否失效,而是在通过 Get 访问主键时才会检查其是否已经失效
    • Redis 定时、定期等多种缓存失效机制,减少内存泄漏
  • 支持的数据类型

    • Memcached支持单一数据类型,[k,v]
    • redis支持五种数据类型

什么是Redis

Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s ,且Redis通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:


  1. 字符串类型 string
  2. 哈希类型 hash
  3. 列表类型 list
  4. 集合类型 set
  5. 有序集合类型 sortedset

redis的应用场景


  • 缓存(数据查询、短连接、新闻内容、商品内容等等)
    • 作为mybatis/hibernate二级缓存(mapper缓存)使用方案,当然其默认也有一级方案,只不过是内存缓存
    • 解释下一级缓存:sqlSession缓存,进程缓存,单次链接有效;当使用了clearCache方法和,或者close方法的话,这个缓存失效,如果还有同样的查询,则还会发送一次查询
  • 聊天室的在线好友列表
  • 任务队列。(秒杀、抢购、12306等等)
  • 应用排行榜
  • 网站访问统计
  • 数据过期处理(可以精确到毫秒
  • 分布式集群架构中的session分离

redis作为数据库的使用有什么优缺点

  • 优点:没有Scheme约束,数据结构的变更相对容易,一开始确定数据类型,抗压能力强,性能极高,10万/qps
  • 缺点:没有索引,没有外键,缺少int/date等基本数据类型,多条件查询需要通过集合内联(sinter,zinterstore) 和连接间接实现开发效率低,可维护性不佳

下载安装方式一


  1. 官网:https://redis.io
  2. 中文网:http://www.redis.net.cn/
  3. 解压直接可以使用:
    • redis.windows.conf:配置文件
    • redis-cli.exe:redis的客户端
    • redis-server.exe:redis服务器端

下载安装方式二

  • 解决could not retrieve mirrorlist,

    • sudo vim /etc/sysconfig/network-scripts/ifcfg-ens33
    • 将ONBOOT改为yes,wq!保存退出
    • 重新启动网络
    $ service network restart
    
  • 安装wget : yum -y install wget

  • 下载redis安装包

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

  • 解压压缩包

tar -zxvf redis-4.0.6.tar.gz

  • yum install gcc
  • 跳转到redis解压目录下 cd redis-4.0.6
  • 编译安装
    • make MALLOC=libc
    • cd src && make install
  • 测试是否安装成功
    cd src ./redis-server

redis三种启动方式以及其中的使用区别

  • 直接启动

cd src ./redis-server

  • 通过指定配置文件启动
    • 首先修改redis.conf文件(在根目录下)的daemonize no为yes
    • 可以输入/然后输入dae按下enter这样就可以搜索到然后保存
    • 进入redis的目录下的src目录,然后执行以下,就是守护进程启动
./redis-server  /usr/local/redis-4.0.6/redis.conf

可以通过下面方式测试是否启动成功 : ./redis-cli -p 6379

  • 补充
    • 针对守护进程启动的方式关闭进程:
    • 查询进程信息 ps -ef|grep redis
    • 关闭进程: kill -9 8370

8370是进程号,而且会有俩进程,找到server那个即可,一般第一个

  • 使用redis启动脚本设置开机自启动
    • linux配置开启自启动脚本都在这个文件夹下面 /etc/init.d,系统启动的时候会自动执行该文件夹下面的脚本,所以把redis启动脚本移动进来即可
    • 启动脚本 redis_init_script 位于Redis的 /utils/ 目录下 (redis提供的)
    • mkdir /etc/redis
    • cp redis.conf /etc/redis/6379.conf
    • 将启动脚本复制到/etc/init.d目录下,本例将启动脚本命名为redisd(通常都以d结尾表示是后台自启动服务)
    • cp redis_init_script /etc/init.d/redisd
    • 设置为开机自启动,直接配置开启自启动 chkconfig redisd on 发现错误: service redisd does not support chkconfig
    • 解决办法,在启动脚本开头添加如下注释来修改运行级别(修改脚本修改的是:/etc/init.d/redisd)
     #!/bin/sh
     # chkconfig:   2345 90 10
    
    • 设置开机自启:chkconfig redisd on
    • 验证是否启动: service redisd start,如果提示已启动则可证明设置开机自启成功了

打开服务: service redisd start #关闭服务 : service redisd stop

命令操作


  1. 选择数据库

redis默认有16个数据库,不选择的话写入默认是0数据库,但是可以手动指定

select 0

  1. redis的数据结构:
  • redis存储的是:key,value格式的数据,其中key都是字符串,value有5种不同的数据结构
  • value的数据结构:
    • 字符串类型 string
    • 哈希类型 hash : map格式
    • 列表类型 list : linkedlist格式。支持重复元素
    • 集合类型 set : 不允许重复元素
    • 有序集合类型 sortedset:不允许重复元素,且元素有顺序
  1. 字符串类型 string
    1. 存储: set key value
      127.0.0.1:6379> set username zhangsan
      OK
    2. 获取: get key
      127.0.0.1:6379> get username
      "zhangsan"
    3. 删除: del key
      127.0.0.1:6379> del age
      (integer) 1
    4. mget:批量获取多个key的值,如果可以不存在则返回nil
    5. incr && incrby :incr对key对应的值进行加加操作,并返回新的值;incrby加指定值
    6. decr && decrby:decr对key对应的值进行减减操作,并返回新的值;decrby减指定值
    7. setnx:设置key对应的值为String类型的value,如果key已经存在则返回0
    8. setex:设置key对应的值为String类型的value,并设定有效期
    9. 其他命令
      • getrange: 获取key对应value的子字符串
      • mset: 批量设置多个key的值,如果成功表示所有值都被设置,否则返回0表示没有任何值被设置
      • msetnx: 同mset,不存在就设置,不会覆盖已有的key
      • getset: 设置key的值,并返回key旧的值
      • append: 给指定key的value追加字符串,并返回新字符串的长度
  2. 哈希类型 hash
  • Hash是一个String类型的field和value之间的映射表,
  • redis的Hash数据类型的key(hash表名称)对应的value实际的内部存储结构为一个HashMap
  • Hash特别适合存储对象。
  • 相对于把一个对象的每个属性存储为String类型,将整个对象存储在Hash类型中会占用更少内存。
  • 所存储的成员较少时数据存储为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。
  • 运用场景:如用一个对象来存储用户信息,商品信息,订单信息等等。
    1. 存储: hset key field value

      • 127.0.0.1:6379> hset myhash username lisi
        (integer) 1
      • 127.0.0.1:6379> hset myhash password 123
        (integer) 1
    2. 获取:

      • hget key field: 获取指定的field对应的值
        127.0.0.1:6379> hget myhash username
        "lisi"
      • hgetall key:获取所有的field和value
        127.0.0.1:6379> hgetall myhash
        • "username"
        • "lisi"
        • "password"
        • "123"
    3. 删除: hdel key field
      127.0.0.1:6379> hdel myhash username
      (integer) 1

    4. hlen--返回key对应的HashMap中的field的数量

  1. 列表类型 list:可以添加一个元素到列表的头部(左边)或者尾部(右边)
    1. 添加:
      1. lpush key value: 将元素加入列表左表

      2. rpush key value:将元素加入列表右边

             127.0.0.1:6379> lpush myList a
             (integer) 1
             127.0.0.1:6379> lpush myList b
             (integer) 2
             127.0.0.1:6379> rpush myList c
             (integer) 3
        
    2. 获取:
      • lrange key start end :范围获取
        127.0.0.1:6379> lrange myList 0 -1
        • "b"
        • "a"
        • "c"
    3. 删除:
      • lpop key: 删除列表最左边的元素,并将元素返回
      • rpop key: 删除列表最右边的元素,并将元素返回
  1. 集合类型 set : 不允许重复元素

    1. 存储:sadd key value
      • 127.0.0.1:6379> sadd myset a
        (integer) 1
      • 127.0.0.1:6379> sadd myset a
        (integer) 0
    2. 获取:smembers key:获取set集合中所有元素
      • 127.0.0.1:6379> smembers myset
        1) "a"
    3. 删除:srem key value:删除set集合中的某个元素
      • 127.0.0.1:6379> srem myset a
        (integer) 1
    4. spop——随机返回并删除key对应的set中的一个元素
    5. suion——求给定key对应的set并集
    6. sinter——求给定key对应的set交集
  2. 有序集合类型 sortedset:不允许重复元素,且元素有顺序.每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

    1. 存储:zadd key score value
      • 127.0.0.1:6379> zadd mysort 60 zhangsan
        (integer) 1
      • 127.0.0.1:6379> zadd mysort 50 lisi
        (integer) 1
      • 127.0.0.1:6379> zadd mysort 80 wangwu
        (integer) 1
    2. 获取:zrange key start end [withscores] , -1表示获取所有元素
      • 127.0.0.1:6379> zrange mysort 0 -1

        • "lisi"
        • "zhangsan"
        • "wangwu"
      • 127.0.0.1:6379> zrange mysort 0 -1 withscores

        • "zhangsan"
        • "60"
        • "wangwu"
        • "80"
        • "lisi"
        • "500"
    3. 删除:zrem key value ,删除key对应的zset中的一个元素
      • 127.0.0.1:6379> zrem mysort lisi
        (integer) 1
    4. zrangebyscore——返回有序集key中,指定分数范围的元素列表,排行榜中运用
    5. zrank——返回key对应的zset中指定member的排名。其中member按score值递增(从小到大);排名以0为底,也就是说,score值最小的成员排名为0,排行榜中运用

set是通过hashmap存储,key对应set的元素,value是空对象;sortset是怎么存储并实现排序的呢,hashmap存储,还加了一层跳跃表;跳跃表:相当于双向链表,在其基础上添加前往比当前元素大的跳转链接

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

  1. 持久化
    1. redis是一个内存数据库,当redis服务器重启,获取电脑重启,数据会丢失,我们可以将redis内存中的数据持久化保存到硬盘的文件中。
    2. redis持久化机制:
      1. RDB:默认方式,不需要进行配置,默认就使用这种机制

        • 在一定的间隔时间中,检测key的变化情况,然后持久化数据
        1. 编辑redis.windwos.conf文件

          • after 900 sec (15 min) if at least 1 key changed
            save 900 1
          • after 300 sec (5 min) if at least 10 keys changed
            save 300 10
          • after 60 sec if at least 10000 keys changed
            save 60 10000
        2. 重新启动redis服务器,并指定配置文件名称
          D:\JavaWeb2018\day23_redis\资料\redis\windows-64\redis-2.8.9>redis-server.exe redis.windows.conf

      2. AOF:日志记录的方式,可以记录每一条命令的操作。可以每一次命令操作后,持久化数据

        1. 编辑redis.windwos.conf文件
          • appendonly no(关闭aof) --> appendonly yes (开启aof)
          • appendfsync always : 每一次操作都进行持久化
          • appendfsync everysec : 每隔一秒进行一次持久化
          • appendfsync no : 不进行持久化

事务

  1. Redis事务可以一次执行多个命令,按顺序串行化执行,且执行过程中不许加塞;
    1. 批量操作在发送 EXEC 命令前被放入队列缓存;
    2. 收到 EXEC 命令后,进入事务执行,事务中任何命令执行失败,其余命令依然被执行;
    3. 在事务执行过程中,其他客户端提交的命令请求不会插入到事务执行序列中;
    4. 总的来说,Redis会将一个事务中的所有命令序列化,然后按顺序执行,且不允许出现加塞行为.
  2. 事务经历的三个阶段:开启事务 --> 命令入列 --> 执行事务;
  3. A向B转账
    multi #开启事务,以后的命令会依次进入到序列中
    get account:a
    get account:b
    decrby account:a 50 #A减50
    incrby account:b 50 #B加50
    exec #执行事务
  4. discard:取消事务,放弃队列中的所有命令;开启了事务,但不想执行 exec 命令时,可执行 discard 命令取消事务;
  5. 错误处理:
    1. 如果队列中的某个命令出现错误,比如操作的key不存在,属于逻辑业务的错误,那么这条命令不再执行,
      而其他命令仍照常执行,而且不会回滚;
    2. 但如果某个命令出现报告错误(语法错误),比如输入的命令不存在,Redis会立即报错,那么Redis会取消事务,
      所有命令都不会执行;
  6. watch/unwatch
    1. 当两个事务同时操作同一个key时,可能会导致数据的不一致;
    2. watch key key ... : 监视key,在开启事务之后、执行事务之前,这个key被其他命令改动了,
      那么当前事务将会被打断,并回滚;
    3. unwatch:取消对所有key的监视;
    4. 如果在执行 watch 命令之后,只要执行了 exec 或 discard 命令,watch会自动取消,无需再执行 unwatch 命令.
  7. 应用场景:
    1. 一组命令必须同时执行,或者都不执行;
    2. 保证一组命令在执行过程中不被其他命令插入;
    3. 商品秒杀活动、转账;

为什么Redis回滚几乎不用

Redis命令在事务中可能会执行失败,但是Redis事务不会回滚,而是继续会执行余下的命令。如果您有一个关系型数据库的知识,这对您来说可能会感到奇怪,因为关系型数据在这种情况下都是会回滚的。

Redis这样做,主要是因为:

只有当发生语法错误(这个问题在命令队列时无法检测到)了,Redis命令才会执行失败, 或对keys赋予了一个类型错误的数据:这意味着这些都是程序性错误,这类错误在开发的过程中就能够发现并解决掉,几乎不会出现在生产环境。
由于不需要回滚,这使得Redis内部更加简单,而且运行速度更快。

缓存问题

  1. 缓存穿透
    1. 查询一个不存在数据时,缓存中没有,就需要从数据库中查询,查不到数据就不写入缓存中,导致每次请求
      这个不存在的数据时,都要到数据库中去查询,造成缓存穿透;
    2. 解决方式:持久层查询不到数据时,就缓存一个空结果(空字符串),每次查询时先判断缓存中是否存在key,
      如果有,则直接返回空值,表示此数据不存在,否则就去查询数据库;
    3. 另外,持久层 insert 此数据时,及时清除查询的key,或者设置空结果的缓存时间;
  2. 缓存雪崩
    1. 如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成缓存雪崩;
    2. 缓存雪崩没有完美的解决方案,应分析用户行为,尽量将缓存失效时间均匀分布;
    3. 大多数系统设计者会考虑加锁/分布式锁,或者队列的方式来保证单线程/进程去写缓存,从而避免失效时
      大量的并发请求落在数据库上;
  3. 热点key
    1. 某个key访问非常频繁,当key失效时,将会有大量线程来构建缓存,导致负载增加,系统崩溃;
    2. 解决方案:
      1. 使用锁,单机使用synchronized、lock等,分布式使用分布式锁;
      2. 不设置缓存过期时间,而是在对应的value里设置一个时间,如果检测到超过了这个时间,则异步更新缓存;
      3. 在value里设置一个比过期时间t0小的过期时间t1,当t1过期时,延长t1,并更新缓存;
      4. 设置标签缓存,并设置过期时间,标签缓存过期后,异步更新实际缓存;
  4. 缓存预热:在项目即将发布上线之前,清空Redis数据库,然后由开发者把所有的功能调试一遍,
    这样Redis中就有了一些缓存数据,然后再发布项目,这样做也是为了避免大量用户同时去查询MySQL数据库了;

Java客户端 Jedis

* Jedis: 一款java操作redis数据库的工具.
* 使用步骤:
    1. 下载jedis的jar包
    2. 使用
            //1. 获取连接
            Jedis jedis = new Jedis("localhost",6379);
            //2. 操作
            jedis.set("username","zhangsan");
            //3. 关闭连接
            jedis.close();

    
* Jedis操作各种redis中的数据结构
    1. 字符串类型 string set get
        ```java
        //1. 获取连接
            Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
            //2. 操作
            //存储
            jedis.set("username","zhangsan");
            //获取
            String username = jedis.get("username");
            System.out.println(username);
    
            //可以使用setex()方法存储可以指定过期时间的 key value
            jedis.setex("activecode",20,"hehe");//将activecode:hehe键值对存入redis,并且20秒后自动删除该键值对
    
            //3. 关闭连接
            jedis.close();
        ``` 
             

    2. 哈希类型 hash : map格式   hset hget hgetAll
        ```java
        //1. 获取连接
            Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
            //2. 操作
            // 存储hash
            jedis.hset("user","name","lisi");
            jedis.hset("user","age","23");
            jedis.hset("user","gender","female");
    
            // 获取hash
            String name = jedis.hget("user", "name");
            System.out.println(name);
    
    
            // 获取hash的所有map中的数据
            Map<String, String> user = jedis.hgetAll("user");
    
            // keyset
            Set<String> keySet = user.keySet();
            for (String key : keySet) {
                //获取value
                String value = user.get(key);
                System.out.println(key + ":" + value);
            }
    
            //3. 关闭连接
            jedis.close();
        ```


    3. 列表类型 list : linkedlist格式。支持重复元素 lpush / rpush lpop / rpop lrange start end : 范围获取
            
        ```java
        //1. 获取连接
            Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
            //2. 操作
            // list 存储
            jedis.lpush("mylist","a","b","c");//从左边存
            jedis.rpush("mylist","a","b","c");//从右边存
    
            // list 范围获取
            List<String> mylist = jedis.lrange("mylist", 0, -1);
            System.out.println(mylist);
            
            // list 弹出
            String element1 = jedis.lpop("mylist");//c
            System.out.println(element1);
    
            String element2 = jedis.rpop("mylist");//c
            System.out.println(element2);
    
            // list 范围获取
            List<String> mylist2 = jedis.lrange("mylist", 0, -1);
            System.out.println(mylist2);
    
            //3. 关闭连接
            jedis.close();
        ```


    4. 集合类型 set  : 不允许重复元素 sadd smembers:获取所有元素

        ```java
        //1. 获取连接
            Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
            //2. 操作
    
    
            // set 存储
            jedis.sadd("myset","java","php","c++");
    
            // set 获取
            Set<String> myset = jedis.smembers("myset");
            System.out.println(myset);
    
            //3. 关闭连接
            jedis.close();
        ```
    5. 有序集合类型 sortedset:不允许重复元素,且元素有顺序 zadd zrange
        ```java
        //1. 获取连接
            Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口
            //2. 操作
            // sortedset 存储
            jedis.zadd("mysortedset",3,"亚瑟");
            jedis.zadd("mysortedset",30,"后裔");
            jedis.zadd("mysortedset",55,"孙悟空");
    
            // sortedset 获取
            Set<String> mysortedset = jedis.zrange("mysortedset", 0, -1);
    
            System.out.println(mysortedset);
    
    
            //3. 关闭连接
            jedis.close();
        ```

jedis连接池: JedisPool

使用:

1. 创建JedisPool连接池对象
2. 调用方法 getResource()方法获取Jedis连接
//0.创建一个配置对象
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(50);
config.setMaxIdle(10);
//1.创建Jedis连接池对象
JedisPool jedisPool = new JedisPool(config,"localhost",6379);
//2.获取连接
Jedis jedis = jedisPool.getResource();
//3. 使用
jedis.set("hehe","heihei");
//4. 关闭 归还到连接池中
jedis.close();

连接池工具类

public class JedisPoolUtils {

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

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

Linux(Centos7)中安装配置使用 Redis

  1. 检查是否有 redis yum 源

  • yum search redis
  • yum info redis

  1. 安装 epel 仓库

EPEL (Extra Packages for Enterprise Linux)是基于 Fedora 的一个项目,为“红帽系”的操作系统提供额外的软件包,适用于 RHEL、CentOS 和 Scientific Linux.

yum install epel-release -y

  1. 安装 redis 数据库

  • yum info redis
  • yum install redis -y

  1. 安装完毕后,使用下面的命令启动 redis 服务

  • systemctl start redis
  • systemctl restart redis
  • systemctl enable redis

  1. linux 上面进入 Redis 客户端

redis-cli

Ubuntu使用编译版本安装

  • step1:下载

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

  • step2:解压

tar -zxvf redis-3.2.8.tar.gz

  • step3:复制,放到usr/local/redis⽬录下

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

  • step4:进⼊redis⽬录

cd /usr/local/redis/

  • step5:生成
    • 安装c语言编译器gcc: sudo apt-get install gcc
    • 安装编译命令make: sudo apt-get install make(这一步可能会出问题,根据提示执行命令)
    • 生成 : sudo make(比较慢)
  • step6:测试,这段运⾏时间会较⻓

sudo make test

会报错:make test error2....

解决方案:

cd 
wget http://downloads.sourceforge.net/tcl/tcl8.6.1-src.tar.gz
sudo tar xzvf tcl8.6.1-src.tar.gz  -C /usr/local/
cd  /usr/local/tcl8.6.1/unix/
sudo ./configure
sudo make(时间比较长)
sudo make install 
  • step7:安装,将redis的命令安装到/usr/local/bin/⽬录

sudo make install (时间比较长)

  • step8:安装完成后,我们进入目录/usr/local/bin中查看,文件如下
a)redis-server      redis服务器
b)redis-cli          redis命令行客户端
c)redis-benchmark  redis性能测试工具
d)redis-check-aof    AOF文件修复工具
e)redis-check-rdb    RDB文件检索工具
  • step9:配置⽂件,移动到/etc/⽬录下
    配置⽂件⽬录为/usr/local/redis/redis.conf

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

配置

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

sudo vi /etc/redis/redis.conf

核心配置选项

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

bind 127.0.0.1

  • 端⼝,默认为6379

port 6379

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

    daemonize yes

  • 数据⽂件

dbfilename dump.rdb

  • 数据⽂件存储路径

dir /var/lib/redis

  • ⽇志⽂件

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

  • 数据库,默认有16个

database 16

  • 主从复制,类似于双机备份。

slaveof

启动服务端

此时启动针对编译安装版本的启动

  • cd /etc/redis/redis-server
  • ./redis.conf

启动客户端

redis-cli

测试是否是通的

ping

返回pong则证明通畅

关闭

查询redis是否已经启动

ps aux | grep redis

ps aux代表显示所有进程,|是代表条件,grep是过滤然后是和redis有关的进程。

  • 然后通过查询回来的pid进行关闭

sudo kill -9 23113/pid

默认指令

不修改配置文件的情况下,默认使用如下指令即可

  • 推荐使⽤服务的⽅式管理redis服务
  • 启动

sudo service redis start

  • 停⽌

sudo service redis stop

  • 重启

sudo service redis restart

但是修改配置文件之后就用不了了,一般推荐如下:

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

注意

如果以 redis start形式开启redis,并且写入数据,此时kill形式关闭,再已
找到配置文件形式启动,则可能启动不成功,此时可以 rm dump.rdb删除redis本地数据库文件之后在启动。原因是两种启动方式,一种使用存储了另外一种大部分情况下不能操作数据库文件,但是不是绝对的。

go中使用redis

安装命令

go get github.com/gomodule/redigo/redis

安装完成后,回到家目录创建test.go,把下面代码复制到test.go里面,编译执行test.go,之后在redis中查找到键c1值为hello,说明安装成功

package main
import ( "github.com/gomodule/redigo/redis")
func main(){
        conn,_ := redis.Dial("tcp", ":6379")
        defer conn.Close()
        conn.Do("set", "c1", "hello")
}

操作方法

Go操作redis文档

  • 连接数据库

Dial(network, address string)(conn,err)

  • 执行数据库操作命令
Send(commandName string, args ...interface{}) error
Flush() error
Receive() (reply interface{}, err error)

Send函数发出指令,flush将连接的输出缓冲区刷新到服务器,Receive接收服务器返回的数据

例如:

c.Send("SET", "foo", "bar")
c.Send("GET", "foo")
c.Flush()//把缓冲区命令发到服务器
c.Receive() // 接收set请求返回的数据
v, err = c.Receive() // 接收get请求传输的数据

另外一种执行数据库操作命令

Do(commandName string, args ...interface{}) (reply interface{}, err error)

  • reply helper functions(回复助手函数)

Bool,Int,Bytes,map,String,Strings和Values函数将回复转换为特定类型的值。为了方便地包含对连接Do和Receive方法的调用,这些函数采用了类型为error的第二个参数。如果错误是非nil,则辅助函数返回错误。如果错误为nil,则该函数将回复转换为指定的类型:

exists, err := redis.Bool(c.Do("EXISTS", "foo"))
if err != nil {
//处理错误代码
}
reflect.TypeOf(exists)//打印exists类型
  • Scan函数

func Scan(src [] interface {},dest ... interface {})([] interface {},error)

Scan函数从src复制到dest指向的值。
Dest参数的值必须是整数,浮点数,布尔值,字符串,[]byte,interface{}或这些类型的切片。Scan使用标准的strconv包将批量字符串转换为数字和布尔类型。

示例代码

var value1 int
var value2 string
reply, err := redis.Values(c.Do("MGET", "key1", "key2"))
if err != nil {
    //处理错误代码
}
 if _, err := redis.Scan(reply, &value1, &value2); err != nil {
    // 处理错误代码
}

序列化与反序列化

针对redis,如果存储复杂数据类型可以序列化之后存储,取出后,反序列化获取。一般都是利用Bytes存储序列化的数据。

  • 序列化(字节化)
var buffer bytes.Buffer//容器
enc :=gob.NewEncoder(buffer)//编码器
err:=enc.Encode(dest)//编码

反序列化(反字节化)

dec := gob.NewDecoder(bytes.NewReader(buffer.bytes()))//解码器
dec.Decode(src)//解码

Nodejs 中使用 Redis

  1. 在你的项目中安装 Redis

npm install redis --save 或者 cnpm install redis --save

  1. 使用 Redis
字符串案例:
var redis = require('redis');
var client = redis.createClient(6379, 'localhost');
//设置数据
client.set('username', '李四');
client.set('username', '李四','EX','5'); //设置过期 5 秒
//获取数据
client.get('username', function(err, val){
console.log(val);
});
列表案例:
var redis = require('redis');
var client = redis.createClient(6379, 'localhost');
client.rpush('testLists', 'a');
client.rpush('testLists', 'b');
client.rpush('testLists', 'c');
client.lpush('testLists', 2);
client.lpush('testLists', 1);
client.lrange('testLists',0, -1,(err,lists)=>{
if(err){
console.log(err);
return;
}
console.log(lists)
})
集合案例:
var redis = require('redis');
var client = redis.createClient(6379, 'localhost');
client.sadd('testSet', 1);
client.sadd('testSet', 'a');
client.sadd('testSet', 'bb');
client.smembers('testSet', function(err, v){
console.log(v);
});
哈希案例:
var redis = require('redis');
var client = redis.createClient(6379, 'localhost');
client.hset('userinfo',"username", "zhangsan");
client.hmset('userinfo',"username","张三","age", "20","sex","男");
client.hgetall('userinfo',function(err,val){
console.log(val);
})

Redis 订阅发布


  • Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
  • redis发布订阅功能比较薄弱但比较轻量级,mq消息持久化,数据可靠性比较差,无后台功能可msgId、msgKey进行查询消息
  • Redis 客户端可以订阅任意数量的频道。
  • 下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1之间的关系:

图1.png

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端

图2.png
  • 发布

client.publish('testPublish', 'message from publish.js');

  • 订阅
client.subscribe('testPublish');
client.on('message', function(channel, msg){
console.log('client.on message, channel:', channel, ' message:', msg);
});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容