`

Redis 入门

 
阅读更多
前言:
日常Java Web中使用数据库来存储,一般不会有高并发的问题,可一旦涉及高并发的情况,
比如商品抢购,主页访问量瞬间较大,使用数据库会产生磁盘读写带来的性能瓶颈,
通过引入NoSql技术基于内存的数据库,可克服上述问题。

Redis是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis与其他key-value缓存产品比较有一下三个特点:
1.Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启后可加载使用。
2.不仅支持简单的key-value类型的数据,还提供list,set,zset,hash等数据结构的存储。
3.支持数据的备份,即master-slave模式的数据备份。

Redis的优势
1.性能极高,支持每秒十几万次读/写操作。
2.丰富的数据类型,支持二级制Strings,Lists,Hashes,Sets及Ordered Sets数据类型操作
3.原子,Redis所有操作都是原子性的,要么成功要么失败不执行,单个操作是原子性,多个操作
也支持事务。
4.丰富的特性,支持publish/subscribe,通知,key过期等等特性。

Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存

Redis能做什么:
1.存储缓存用的数据
2.需要高速读/写的场合使用它快速读/写

Redis不能做什么
1.数据量太大不适合
2.数据访问频率太低不适合

缓存的读写:
日常读写比例大约在1:9到3:7,读比写占的比重大,当使用数据库SQL语句读写时,
数据读写磁盘的速度相对较慢,若放在Redis中服务端可直接读取内存中的数据,速度提升较多,数据库压力减少,
但同时考虑内存的大小问题,一般只是使用Redis存储常用和主要的数据,比如用户登录信息。
一般使用Redis存储时,会考虑以下方面:
1.该业务数据常用么?命中率如何? 若命中率低,没有必要写入缓存;
2.该业务读操作多,还是写操作多? 若频繁需要写入数据库,也没有必要;
3.业务数据大小如何? 若存储几百兆文件会给缓存带来很大压力。

Redis缓存读取逻辑:
当第一次读取Redis数据时会找不到,触发程序读取数据库,把数据读取出来并写入Redis。
当第二次需要读取时会直接读取Redis,读到数据结束流程。

根据以上可知,Redis使用读大于写操作,同时经常读取且命中率较高的场景。

Redis缓存更新操作:
Redis->更新/写入数据库->更新/写入Redis
当Redis更新或者写入操作,需要多个Redis的操作,若业务数据写次数大于读次数则
没有必要使用Redis。

Redis高速读写场合逻辑:
当一个请求到达服务器时,只是把业务数据库在Redis上读写,没有对数据库进行任何操作。
大大提高读写速度。
但这些缓存数据需要持久化,所以请求操作完Redis读/写后会判断该高速读/写业务是否结束,
这个判断通常在秒杀商品为0/红包金额为0时成立,若不成立则不会操作数据库,
若成立则触发事件将Redis的缓存数据以批量的形式一次性写入数据库,完成持久化工作。

下载Redis压缩包:
https://github.com/ServiceStack/redis-windows/tree/master/downloads
解压后新建一个startup.cmd的文件,填写redis-server redis.windows.conf
这个命令是在调用redis-server.exe命令来读取redis.window.conf的内容。
启动redis-server后,打开redis-cli.exe客户端工具,输入以下表示测试成功。
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> get key1
"value1"

创建java Redis项目
Jedis是一个非常小而且正常的Redis java客户端,复制以下pom内容到maven项目中,
https://mvnrepository.com/artifact/redis.clients/jedis/2.9.0
import redis.clients.jedis.Jedis;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class HelloWorld {
    public static void main(String[] args) {
        //连接本地的 Redis 服务
        Jedis jedis = new Jedis("localhost");
        System.out.println("连接成功");
        jedis.set("runobj","yangeoo.iteye.com");
        //查看服务是否运行
        System.out.println("Redis存储的字符串: "+jedis.get("runobj"));
        //设置List字符串
        jedis.lpush("site-list", "Runoob");
        jedis.lpush("site-list", "Google");
        jedis.lpush("site-list", "Taobao");
        // 获取存储的数据并输出
        List<String> list = jedis.lrange("site-list", 0 ,2);
        for(int i=0; i<list.size(); i++) {
            System.out.println("列表项为: "+list.get(i));
        }
        // 获取数据并输出
        Set<String> keys = jedis.keys("*");
        Iterator<String> it=keys.iterator() ;
        while(it.hasNext()){
            String key = it.next();
            System.out.println(key);
        }
    }
}

Redis连接数据库:
redis-cli -h host -p port -a password



Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

String(字符串)
string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。

示例
127.0.0.1:6379> set runoob "HelloWorld!"
OK
127.0.0.1:6379> get runoob
"HelloWorld!"
127.0.0.1:6379> set runoob "123"
OK
127.0.0.1:6379> del runoob  //删除runoob变量
(integer) 1
127.0.0.1:6379> get runoobj
(nil)

Hash(哈希)
Redis hash 是一个键值(key=>value)对集合。
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象
实例中我们使用了 Redis HMSET, HGET 命令,HMSET 设置了两个 field=>value 对, HGET 获取对应 field 对应的 value。
每个hash可以存储键值对(40多亿).

127.0.0.1:6379> HMSET runoob field1 "Hello" field2 "World!"
(error) WRONGTYPE Operation against a key holding the wrong kind of value //DEL runoob 用于删除前面测试用过的 key,不然会报错
127.0.0.1:6379> del runoob
(integer) 1
127.0.0.1:6379> HMSET runoob field1 "Hello" field2 "World!"
OK
127.0.0.1:6379> hget runoob field1
"Hello"
127.0.0.1:6379> hget runoob field2
"World!"

List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
列表最多可存储4294967295元素

127.0.0.1:6379> lpush runoob redis
(integer) 1
127.0.0.1:6379> lpush runoob mongodb
(integer) 2
127.0.0.1:6379> lpush runoob rabitmq
(integer) 3
127.0.0.1:6379> lpush runoob redis
(integer) 4
127.0.0.1:6379> lrange runoob 0 10
1) "redis"
2) "rabitmq"
3) "mongodb"
4) "redis"

Redis 的 Set 是 string 类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
sadd 命令
添加一个 string 元素到 key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返回 0。
sadd key member
集合中最大可存储40多亿个成员。

127.0.0.1:6379> del runoob
(integer) 1
127.0.0.1:6379> sadd runoob redis
(integer) 1
127.0.0.1:6379> sadd runoob mongodb
(integer) 1
127.0.0.1:6379> sadd runoob rabitmq 
(integer) 1
127.0.0.1:6379> sadd runoob rabitmq //添加相同的已存在的元素则返回0
(integer) 0
127.0.0.1:6379> smembers runoob
1) "mongodb"
2) "rabitmq"
3) "redis"


zset(sorted set:有序集合)
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。

zadd 命令
添加元素到集合,元素在集合中存在则更新对应score
zadd key score member 

127.0.0.1:6379> del runoob
(integer) 1
127.0.0.1:6379> zadd runoob 1 redis
(integer) 1
127.0.0.1:6379> zadd runoob 3 rabitmq
(integer) 1
127.0.0.1:6379> zadd runoob 2 redis
(integer) 0
127.0.0.1:6379> zadd runoob 3 oracle
(integer) 1
127.0.0.1:6379> zrangebyscore runoob 0 1000
1) "redis"
2) "oracle"
3) "rabitmq"
127.0.0.1:6379> zadd runoob 4 oracle
(integer) 0
127.0.0.1:6379> zrangebyscore runoob 0 1000
1) "redis"
2) "rabitmq"
3) "oracle"

应用场景:
String字符串 : 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M
Hash(字典):键值对集合,即编程语言中的Map类型,适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值,适用场景:用于存储、读取、修改用户属性。
List(列表): 增删快,提供了操作某一段元素的API,适用场景:1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列
Set(集合):哈希表实现,元素不重复1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作。
适用场景:1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐
Sorted Set(有序集合):将Set中的元素增加一个权重参数score,元素按score有序排列。数据插入集合时,已经进行天然排序。	1、排行榜 2、带权重的消息队列

Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享。
一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中,可以把每个字典都理解成一个独立的数据库。
每个数据库对外都是从0开始的递增数字命名,默认支持16个数据库,客户端连接自动选择0数据库,可以通过SELECT命令更换。比如
选择1号数据库:
select 1
开发者必须自己记录哪些数据库存储了哪些数据。另外Redis也不支持为每个数据库设置不同的访问密码,
所以一个客户端要么可以访问全部数据库,要么连一个数据库也没有权限访问。最重要的一点是多个数据库之间并不是完全隔离的,
比如FLUSHALL命令可以清空一个Redis实例中所有数据库中的数据.
综上所述,这些数据库更像是一种命名空间,而不适宜存储不同应用程序的数据.

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

client1:
127.0.0.1:6379> subscribe redisChat   //订阅redisChat频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
1) "message"
2) "redisChat"
3) "123"

client2:
127.0.0.1:6379> subscribe redisChat //订阅redisChat频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
1) "message"
2) "redisChat"
3) "Redis is a great caching technique"
1) "message"
2) "redisChat"
3) "Learn redis by runoob.com"
1) "message"
2) "redisChat"
3) "123"

client3: //向redisChat发布消息
127.0.0.1:6379> publish redisChat "Redis is a great caching technique"
(integer) 1
127.0.0.1:6379> publish redisChat "Learn redis by runoob.com"
(integer) 1
127.0.0.1:6379> publish redisChat "123"
(integer) 2

Redis事务
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
批量操作在发送 EXEC 命令前被放入队列缓存。
收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:

开始事务。
命令入队。
执行事务

以下是一个事务的例子,它先以 MULTI 开始一个事务,然后将多个命令入队到事务中,最后由 EXEC 命令触发事务,一并执行事务中的所有命令:
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set book-name "Mastering C++ in 21 days"
QUEUED
127.0.0.1:6379> get book-name
QUEUED
127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
127.0.0.1:6379> SMEMBERS tag
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
   2) "Programming"
   3) "C++"

单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做


Redis分区
分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。

分区的优势
通过利用多台计算机内存的和值,允许我们构造更大的数据库。
通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽。


分区的不足
redis的一些特性在分区方面表现的不是很好:

涉及多个key的操作通常是不被支持的。举例来说,当两个set映射到不同的redis实例上时,你就不能对这两个set执行交集操作。
涉及多个key的redis事务不能使用。
当使用分区时,数据处理较为复杂,比如你需要处理多个rdb/aof文件,并且从多个实例和主机备份持久化文件。
增加或删除容量也比较复杂。

分区类型
范围分区
最简单的分区方式是按范围分区,就是映射一定范围的对象到特定的Redis实例。
比如,ID从0到10000的用户会保存到实例R0,ID从10001到 20000的用户会保存到R1,以此类推。
这种方式是可行的,并且在实际中使用,不足就是要有一个区间范围到实例的映射表。这个表要被管理,同时还需要各种对象的映射表,
通常对Redis来说并非是好的方法。

哈希分区
另外一种分区方法是hash分区。这对任何key都适用,也无需是object_name:这种形式,像下面描述的一样简单:
用一个hash函数将key转换为一个数字,比如使用crc32 hash函数。对key foobar执行crc32(foobar)会输出类似93024922的整数。
对这个整数取模,将其转化为0-3之间的数字,就可以将这个整数映射到4个Redis实例中的一个了。93024922 % 4 = 2,
就是说key foobar应该被存到R2实例中。注意:取模操作是取除的余数,通常在多种编程语言中用%操作符实现。

 

 

分享到:
评论

相关推荐

    REDIS 入门指南pdf

    以下是对 Redis 入门指南的一些关键知识点的详细说明: 1. **Redis 数据类型** - **字符串(String)**:Redis 最基本的数据类型,可以存储字符串、数字等,支持自增、自减操作。 - **哈希(Hash)**:用于存储...

    Redis 入门指南.pdf

    本指南将帮助初学者理解 Redis 的基本概念、安装与配置、数据类型以及常用命令,从而快速入门 Redis。 1. **Redis 简介** - Redis 源自意大利程序员 Salvatore Sanfilippo,最初是为了提高网站性能而设计的。 - ...

    Redis入门指南(第2版)

    本书是一本Redis入门指导书籍,以通俗易懂的方式介绍了Redis基础与实践方面的知识,包括历史特性、在开发和生产环境中部署运行Redis、数据类型与命令、使用Redis实现队列、事务、复制、管道、持久化、优化Redis存储...

    Redis入门指南(第2版)电子书

    《Redis入门指南(第2版)》是一本Redis的入门指导书籍,以通俗易懂的方式介绍了Redis基础与实践方面的知识,包括历史与特性、在开发和生产环境中部署运行Redis、数据类型与命令、使用Redis实现队列、事务、复制、...

    Redis入门到精通最新教学视频

    Redis入门到精通最新教学视频!!!!!!!!!!!!!!!!!!!

    Redis入门指南 第2版 高清PDF完整扫描版.pdf 【带书签】

    Redis入门指南 第2版 高清PDF完整扫描版.pdf 。搜集整理自互联网,仅供个人学习,严禁用于商业用途,如有版权问题,请联系删除。

    redis入门指南2

    《Redis入门指南(第2版)》是一本Redis的入门指导书籍,以通俗易懂的方式介绍了Redis基础与实践方面的知识,包括历史与特性、在开发和生产环境中部署运行Redis、数据类型与命令、使用Redis实现队列、事务、复制、...

    redis入门指南

    《Redis入门指南》是一本Redis的入门指导书籍,以通俗易懂的方式介绍了Redis基础与实践方面的知识,包括历史与特性、在开发和生产环境中部署运行Redis、数据类型与命令、使用Redis实现队列、事务、复制、管道、持久...

    Redis入门指南

    《Redis入门指南》是一本Redis的入门指导书籍,以通俗易懂的方式介绍了Redis基础与实践方面的知识,包括历史与特性、在开发和生产环境中部署运行Redis、数据类型与命令、使用Redis实现队列、事务、复制、管道、持久...

    redis入门指南 高清PDF

    redis 入门教程,高清扫描版。免费下载、

    Redis入门指南第2版+带书签

    **Redis入门指南** Redis,全称Remote Dictionary Server,是一款开源的、高性能的键值存储系统。它被广泛用于数据缓存、消息队列、数据库等多个领域。本指南将基于《Redis入门指南第2版》的内容,详细介绍Redis的...

    Redis入门简单实例

    Redis入门简单实例,附带详细说明,代码可直接运行,欢迎交流。

    Redis入门第二版

    **Redis入门第二版** Redis,全称Remote Dictionary Server,是一个开源的、高性能的键值存储系统,常被用作数据库、缓存和消息中间件。它支持多种数据结构,如字符串、哈希、列表、集合、有序集合等,这使得Redis...

    Redis入门指南 - 李子骅.mobi

    《Redis入门指南》是一本Redis的入门指导书籍,以通俗易懂的方式介绍了Redis基础与实践方面的知识,包括历史与特性、在开发和生产环境中部署运行Redis、数据 ...

Global site tag (gtag.js) - Google Analytics