Redis入门
1、NoSQL概述【重点了解】
1.1 什么是NoSQL
NoSQL(NoSQL=Not Only SQL),意即"不仅仅是SQL"是一项全新的数据库理念,泛指非关系型数据库。
【关系型数据库】
关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织
优点:
1、易于维护:都是使用表结构,格式一致;
2、使用方便:SQL语言通用,可用于复杂查询;
3、复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。
缺点:
1、读写性能比较差,尤其是海量数据的高效率读写;
2、固定的表结构,灵活度稍欠;
3、高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。// 35万/s
【非关系型数据库】
非关系型数据库严格上讲不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。
优点:
1、格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
2、速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
3、高扩展性;
4、成本低:nosql数据库部署简单,基本都是开源软件。
缺点:
1、不提供sql支持,学习和使用成本较高;
2、无事务处理;
3、数据结构相对复杂,复杂查询方面稍欠
1.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小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数据迁移。
1.3 主流的NoSQL产品
【NoSQL数据库分类】
- 键值(Key-Value)存储数据库;Map
- 列存储数据库;
- 文档型数据库;
- 图形(Graph)数据库;
1、【键值(Key-Value)存储】
相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
典型应用: 内容缓存,主要用于处理大量数据的高访问负载。
数据模型: 一系列键值对
优势: 快速查询
劣势: 存储的数据缺少结构化
2、【列存储数据库】
相关产品:Cassandra, HBase, Riak
典型应用:分布式的文件系统
数据模型:以列簇式存储,将同一列数据存在一起
优势:查找速度快,可扩展性强,更容易进行分布式扩展
劣势:功能相对局限
3、【文档型数据库】
相关产品:CouchDB、MongoDB
典型应用:Web应用(与Key-Value类似,Value是结构化的)
数据模型: 一系列键值对
优势:数据结构要求不严格
劣势: 查询性能不高,而且缺乏统一的查询语法
4、【图形数据库】
相关数据库:Neo4J、InfoGrid、Infinite Graph
典型应用:社交网络
数据模型:图结构
优势:利用图结构相关算法。
劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。
1.4 NoSQL的特点
NoSQL数据库在大数据存储上具有关系型数据库无法比拟的优势:
1、易扩展
NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。也无形之间,在架构的层面上带来了可扩展的能力。
2、大数据量,高性能
NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
3、灵活的数据模型
NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的Web2.0时代尤其明显。
4、高可用
NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如Cassandra,HBase模型,通过复制模型也能实现高可用。
综上所述,NoSQL的非关系特性时期成为了后web2.0时代的宠儿,助力大型的web2.0网站的再次起飞,是一项全新的数据库革命性运动。
web1.0—传播信息(静态网页)
web2.0—微博(交互性)-- 功能实现,用户体验(性能–NoSQL)
2、Redis概述
2.1 Redis的由来
2008年,意大利的一家创业公司Merzia推出了一款基于MySQL的网站实时统计系统LLOOGG,然而没过多久该公司的创始人Salvatore Sanfilippo便对MySQL的性能感到失望,于是他决定亲自为LLOOGG量身订做一个数据库,并于2009年开发完成,这个数据库就是Redis。不过Salvatore Sanfilippo并不满足只将Redis用于LLOOGG这一款产品,而是希望更多的人使用它,于是在同一年Salvatore Sanfilippo将Redis开源发布,并开始和Redis的另一名主要的代码贡献者Pieter Moordhuis一起继续着Redis的开发,今天。
Salvatore Sanfilippo自己也没想到,短短的几年时间,Redis就拥有了庞大的用户群体。Hacker News在2012年发布了一份数据库的使用情况调查,结果显示有近12%的公司在使用Redis。国内如新浪微博,街旁网,知乎网,国外如GitHub,Stack Overflow,Flickr等都是Redis的用户。
VMware公司从2010年开始赞助Redis的开发,Salvatore Sanfilippo和Pieter Noordhuis也分别在3月和5月加入VMware,全职开发Redis。
2.2 什么是Redis
Redis是用C语言开发的一个开源的高性能键值对(Key-Value)数据库。它通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的值数据类型如下:
1. 字符串类型 string(常用:json/xml)
2. 散列类型 hash(key--value)map
3. 列表类型 list linkedlist 用户列表
4. 集合类型 set
5. 有序集合类型 sortedset
Redis: 软件--C语言--存储数据
特点:key-value ,内存存储
2.3 Redis的应用场景
Redis在很多方面与其他数据库解决方案不同:它使用内存提供主存储支持,而仅使用硬盘做持久性的存储;在一些需要大容量数据集的应用,Redis并不适合,因为它的数据集不会超过系统可用的内存。我们通常把它融入到系统中来,这就能够解决很多问题,比如那些你现有的数据库处理起来感到缓慢的任务。这些你就可以通过Redis来进行优化,或者为应用创建些新的功能。
1、缓存(数据查询,端链接,新闻内容,商品内容等等);--使用最多
2、聊天室的在线好友列表;
3、任务队列;(秒杀,抢购,12306等等)
4、应用排行榜;
5、网站访问统计;
6、数据过期处理(可以精确到毫秒);
7、分布式集群架构中的session分离;
3、window版Redis的安装与使用
3.1 window版Redis下载
官方提倡使用Linux版的Redis,所以官网只提供了Linux版的Redis下载,我们可以从GitHub上下载window版的Redis,具体链接地址如下:
官网下载地址: http://redis.io/download
github下载地址: https://github.com/MSOpenTech/redis/tags
3.2 window版Redis的目录结构
将上述压缩包解压后得到如下目录:
【Redis目录介绍】
目录或文件 | 作用 |
---|---|
redis-benchmark | 性能测试工具 |
redis-check-aof | AOF文件修复工具 |
redis-check-dump | RDB文件检查工具(快照持久化文件) |
redis-cli | 命令行客户端 |
redis-server | redis服务器启动命令 |
redis.windows.conf | redis核心配置文件 |
3.3 window版Redis的安装
window版的Redis是免安装的,将压缩包解压完成即可使用。
3.4 window版Redis的启动与关闭
启动:双击redis-server.exe
关闭:Ctrl+C 或者 将服务窗口关闭
3.5 连接Redis服务
Redis数据库服务的连接有两种方式:
- 命令行工具:
- 图形化界面工具:
【命令行工具使用】
双击redis-cli.exe文件,会弹出命令行客户端窗口,我们可以在这个窗口中书写redis的命令:ping 。如果服务器能够返回PONG说明,连接成功!
【图形化界面工具】
开发中,我们往往会使用图形化界面工具连接Redis服务,对Redis中的数据进行更加直观地操作。图形化界面工具是一个独立的软件,使用之前需要先进行安装。软件在资料中:
【使用步骤】
-
安装:双击,下一步即可完成安装;
-
连接:新建连接,配置连接信息;
-
查看Redis实例:双击刚刚创建的连接,如果出现16个db实例,即说明连接成功。
【注意】
- redis服务器中默认的数据库数量是16,如果不指定数据库,默认使用id为0的数据库;
- 切换数据库的命令:SELECT ;
4、Redis的数据结构
Redis是一种高级的Key-Value类型的存储系统,其中key值是字符串类型,value值支持5种常用的数据类型。
redis中文网:http://www.redis.net.cn/order/
4.1 Redis中的Key值
Redis中的key值是字符串类型,key值是Redis中数据查询和存储的依据。我们在往Redis中存储数据时,关于key值的命名需要注意以下几点:
key不要太长,最好不要超过1024个字节,这不仅会消耗内存还会降低查找效率
key不要太短,如果太短会降低key的可读性
key命名规范,在项目中key最好有一个统一的命名规范
4.2 Redis中的Value值
Redis中的Value值是用来存储具体数据的,常用的数据类型有以下5种:
· 字符串(String)
· 哈希(hash)
· 字符串列表(list)
· 字符串集合(set)
· 有序字符串集合(sorted set)
在日常开发中主要使用比较多的有字符串、哈希、字符串列表、字符串集合四种类型,其中最为常用的是字符串类型。
4.3 字符串类型(String)
应用场景:
常规key-value缓存应用。常规计数: 微博数, 粉丝数,json格式的数据。
1. 字符串类型string概述
字符串类型是Redis中最为基础的数据存储类型,字符串在Redis中是二进制保存,因此是安全的,这便意味着该类型存入和获取的数据相同。
在Redis中字符串类型的Value最多可以容纳的数据长度是512M。
2. 字符串类型string常用命令
功能 | 命令 | 说明 |
---|---|---|
设置(修改)值 | set key value | 该key存在则进行覆盖操作,该操作总是返回"OK"。 |
获取值 | get key | 获取该key关联的字符串value值。如果value值不是字符串 会报错,如果key不存在则返回nil。 |
删除数据 | del key [key2 key3…] | 根据指定的key删除对应的数据。可一次删除多个 |
批量设置值 | mset key1 value1 key2 value2 | 同时设置多对键值对 |
批量取值 | mget key1 key2 | 同时获取过个key值对应的value值 |
设置值(返回原来的值) | getset key value | 给指定的key设置value值,返回原来的值(如果原来没有值返回nil) |
注:查看redis中的所有key值: keys *
【命令】 :set key value
设定key持有指定的字符串value,如果该key存在则进行覆盖操作。总是返回”OK”。
注意:value值可以可以不用添加双引号。value值在redis中都是以字符串的形式进行存储;
127.0.0.1:6379> set name "zhangsan"
OK
127.0.0.1:6379> set addr shanghai
OK
127.0.0.1:6379> set age 13
OK
【命令】 : get key
获取key的value。如果与该key关联的value不是String类型,redis将返回错误信息,因为get命令只能用于获取String value;如果该key不存在,返回(nil)。
127.0.0.1:6379> get name
"zhangsan"
127.0.0.1:6379> get addr
"shanghai"
127.0.0.1:6379> get age
"13"
127.0.0.1:6379> get company
(nil)
【命令】 : del key
根据指定的key删除对应的数据。
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> del name
(integer) 0
【扩展】
- 设置多个值:mset key1 value1 [key2 value2 …]
127.0.0.1:6379> mset name zhangsan age 13
OK
- 获取多个值:mget key1 [key2…]
127.0.0.1:6379> mget name age
1) "zhangsan"
2) "13"
- 设置值(返回原值):getset key value
127.0.0.1:6379> getset name lisi
"zhangsan"
127.0.0.1:6379>
4.4 Hash类型数据
4.4.1 Hash类型数据概述
Redis中的Hash类型可以看成具有String Key和String Value的map容器。所以该类型非常适合于存储值对象的信息。如username、password和age等。如果Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash可以存储4294967295个键值对。
Key是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field),
也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。
4.4.2. 哈希类型hash常用命令
使用场景:
存储部分变更数据,如用户信息等。
常用命令
功能 | 命令 | 说明 |
---|---|---|
为指定的key设定field/value | hset key field value | 设置单个值 |
获取指定key的field对应的value值 | hget key field | 获取单个值 |
删除指定key的field对应的value值 | hdel key field [field2 …] | 可以删除多个值 |
为指定的key批量设置值 | hmset key field value [field2 value2] | 批量设置值 |
获取指定key的多个值 | hmget key field[field2 field3 …] | 批量获取值 |
获取指定key的所有键值对 | hgetall key | 获取所有的键值对 |
【命令】 : **hset key field value **
为指定的key设定field/value对(键值对)。
127.0.0.1:6379> hset stu name zhangsan
(integer) 1
127.0.0.1:6379>
【命令】 hget key field
返回指定的key中的field的值
127.0.0.1:6379> hget stu name
"zhangsan"
127.0.0.1:6379>
【命令】 hmset key field value[field2 value2 …]
批量给指定key设置多个key-value值
127.0.0.1:6379> hmset stu name zhangsan age 13
OK
127.0.0.1:6379>
【命令】 hmget key field[field2 …]
批量获取指定key的多个key-value值
127.0.0.1:6379> hmget stu name age
1) "zhangsan"
2) "13"
127.0.0.1:6379>
【命令】 **hgetall key **
批量获取指定key的所有key-value值
127.0.0.1:6379> hgetall stu
1) "name"
2) "zhangsan"
3) "age"
4) "13"
127.0.0.1:6379>
【命令】 hdel key field [field2 … ]
可以删除一个或多个字段,返回值是被删除的字段个数
127.0.0.1:6379> hdel stu name age
(integer) 2
127.0.0.1:6379>
4.5 列表类型list
应用场景:
list集合: 存取有序(存入时的顺序),有索引,可以重复 ------- 查询快,增删慢
Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。
List 就是链表,相信略有数据结构知识的人都应该能理解其结构。使用List结构,我们可以轻松地实现最新消息排行等功能。List的另一个应用就是消息队列,可以利用List的PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作List中某一段的api,你可以直接查询,删除List中某一段的元素。
4.5.1. 列表类型list概述
在Redis中,List类型是按照插入顺序排序的字符串链表,可保存重复数据。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。
在插入时,如果该键不存在,Redis将为该键创建一个新的链表。
如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。
List中可以包含的最大元素数量是4294967295
4.5.2 列表类型list常用命令
功能 | 命令 | 说明 |
---|---|---|
头部插入数据 | lpush key value1 value2… | 从list的头部开始插入数据 |
尾部插入数据 | rpush key value1 value2… | 从list的尾部开始插入数据 |
查看list中所有数据 | lrange key 0 -1 | 索引0指的是列表中第一个元素,-1指列表中最后一个元素 |
查看list中指定索引区间的元素 | lrange key start end | start:开始索引,从0开始;end:结束索引; |
查看指定索引的值 | lindex key index | index:值在list中的索引 |
从list的头部弹出一个值 | lpop key | 获取并移除list头部的第一个值 |
从list的尾部弹出一个值 | rpop key | 获取并移除list头部的最后一个值 |
【命令-头部插入】 lpush key value1 value2 …
在指定的key所关联的list的头部插入所有的values,如果该key不存在,该命令在插入的之前创建一个与该key关联的空链表,之后再向该链表的头部插入数据。如果插入成功,返回元素的个数。
127.0.0.1:6379> lpush stus zhagnsan lisi wangwu
(integer) 3
127.0.0.1:6379>
【命令-尾部插入】 rpush key value value value
在指定的key对应的list的尾部插入所有的value,如果该key不存在,该命令在插入之前创建一个与该key对应的空链表,再从尾部插入数据。如果插入成功,返回元素的个数。
127.0.0.1:6379> rpush strs zhangfei guanyu liubei
(integer) 3
127.0.0.1:6379>
【命令-查看列表】 lrange key 0 -1 和 lrange key start end
查看list中的所有数据;
127.0.0.1:6379> lrange stus 0 -1
1) "wangwu"
2) "lisi"
3) "zhagnsan"
127.0.0.1:6379>
查看list中索引为start到end的数据;
127.0.0.1:6379> lrange stus 0 2
1) "wangwu"
2) "lisi"
3) "zhagnsan"
127.0.0.1:6379>
【命令-从list头部取出元素】 lpop key
返回并弹出指定的key关联的链表中的第一个元素,即头部元素。
如果该key不存在,返回nil;
若key存在,则返回链表的头部元素。
127.0.0.1:6379> lpop stus
"wangwu"
127.0.0.1:6379>
【命令-从list尾部取出元素】rpop key
从尾部弹出元素。
127.0.0.1:6379> rpop stus
"zhagnsan"
127.0.0.1:6379>
5.6 集合类型set
set集合: 存取无序 ,不重复 --增删快,查询慢
应用场景:
Redis set对外提供的功能与list类似是一个列表的功能, 特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
5.6.1 集合类型set
在Redis中,我们可以将Set类型看作为没有排序的字符集合,和List类型一样,我们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要说明的是,这些操作的时间复杂度为O(1),即常量时间内完成次操作。Set可包含的最大元素数量是4294967295。
和List类型不同的是,Set集合中不允许出现重复的元素。
5.6.2 集合类型set的常用命令
功能 | 命令 | 说明 |
---|---|---|
设置值 | sadd key value [value1,value2…] | |
查看set中的所有值 | smembers key | |
移除并返回集合中的任意一个元素 | spop key | |
删除值【set集合中是元素删除完后set消失】 | srem key members[member1、member2…] | |
获取集合中的成员数 | scard key |
【命令-设置值】sadd key values[value1,value2…]
向set中添加数据,如果该key的值已有则不会重复添加
127.0.0.1:6379> sadd myset zhagnsan lisi wangwu
(integer) 3
127.0.0.1:6379>
【命令-获取值】smembers key
获取set中所有的成员
127.0.0.1:6379> smembers myset
1) "wangwu"
2) "lisi"
3) "zhagnsan"
127.0.0.1:6379>
【命令-删除值】 srem key members[member1、member2…]
删除set中指定的成员
127.0.0.1:6379> srem myset wangwu lisi
(integer) 2
127.0.0.1:6379> smembers myset
1) "zhagnsan"
127.0.0.1:6379>
5.7 有序集合 sorted set
5.7.1 有序集合sorted set应用场景
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。它用来保存需要排序的数据。例如排行榜,一个班的语文成绩,一个公司的员工工资,一个论坛的帖子等。有序集合中,每个元素都带有score(权重),以此来对元素进行排序。它有三个元素:key、member和score。以语文成绩为例,key是考试名称(期中考试、期末考试等),member是学生名字,score是成绩。
有序集合有两大基本用途:排序和聚合
5.7.2 sorted set的常用命令
功能 | 命令 | 说明 |
---|---|---|
添加一个或多个成员 | zadd key score1 member1[score2 member2 …] | |
获取有序集合的成员数 | zcard key | |
通过索引区间返回有序集合成指定区间内的成员 | zrange key start stop [withscores] | |
通过索引区间返回有序集合成指定区间内的成员【顺序排列】 | zrange key start stop withscores | |
通过索引区间返回有序集合成指定区间内的成员【倒序排列】 | zrevrange key start stop withscores | |
移除有序集合中的一个或多个成员 | zrem key member[member1 … ] |
【命令-设置值】zadd key score member[score1 member1,score2 member2…]
往有序集合中添加元素: score member
127.0.0.1:6379> zadd scores 60 chinese 100 english 80 math
(integer) 3
127.0.0.1:6379>
【命令-获取集合成员数】zcard key
127.0.0.1:6379> zcard scores
(integer) 3
127.0.0.1:6379>
【命令-查询集合中的元素】zrange key start stop [withscores]
注:zrange key 0 -1 是查询所有元素;
127.0.0.1:6379> zrange scores 0 2
1) "chinese"
2) "math"
3) "english"
127.0.0.1:6379> zrange scores 0 2 withscores
1) "chinese"
2) "60"
3) "math"
4) "80"
5) "english"
6) "100"
127.0.0.1:6379> zrange scores 0 -1
1) "chinese"
2) "math"
3) "english"
127.0.0.1:6379>
【命令-删除集合中的元素】 zrem key member[member1 … ]
127.0.0.1:6379> zrem scores math english
(integer) 2
127.0.0.1:6379> zrange scores 0 -1
1) "chinese"
127.0.0.1:6379>
5. Redis的通用命令
【查询key】 keys pattern
获取所有与pattern匹配的key,返回所有与该key匹配的keys。*表示任意一个或多个字符,?表示任意一个字符
127.0.0.1:6379> keys *
1) "stu"
2) "strs"
3) "mylist"
4) "scores"
5) "cityData"
6) "stus"
7) "myset"
8) "name"
127.0.0.1:6379> keys name
1) "name"
127.0.0.1:6379>
【根据key值删除】del key1 key2…
删除指定key值的数据
127.0.0.1:6379> del company
(integer) 1
【判断key值是否存在】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>
【获取指定key的数据类型】type key
获取指定key的类型。该命令将以字符串的格式返回。 返回的字符串为string、list、set、hash,如果key不存在返回none
127.0.0.1:6379> type name
string
127.0.0.1:6379> type strs
list
127.0.0.1:6379> type stu
hash
127.0.0.1:6379> type myset
set
127.0.0.1:6379> type scores
zset
http://www.redis.net.cn/order/
6. Jedis的基本使用
6.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)
6.2. jedis的基本操作
6.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 |
6.2.2. jedis的基本操作
导入开发包:
package com.heima.jedis;
import redis.clients.jedis.Jedis;
public class JedisTest {
public static void main(String[] args) {
//1.创建redis核心对象:arg1-host arg2-端口
Jedis jedis = new Jedis("localhost",6379);
//2.存值
jedis.set("name","黑马程序员");
//3.取值
String name = jedis.get("name");
System.out.println(name);
//4.释放资源
jedis.close();
}
}
7.3. jedis连接池的使用
7.3.1. jedis连接池的基本概念
jedis连接资源的创建与销毁是很消耗程序性能,所以jedis为我们提供了jedis的池化技术--jedisPool。
jedisPool在创建时初始化一些连接资源存储到连接池中,使用jedis连接资源时不需要创建。
而是从连接池中获取一个资源进行redis的操作,使用完毕后,不需要销毁该jedis连接资源,而是将该资源归还给连接池,供其他请求使用。
7.3.2. jedisPool的基本使用
package com.heima.jedis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisPoolTest {
public static void main(String[] args) {
//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 com.heima.utils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ResourceBundle;
/**
* Jedis工具类
*/
public class JedisUtil {
private static JedisPool jedisPool;
private static int maxtotal;
private static int maxwaitmillis;
private static String host;
private static int port;
//加载配置文件
static {
ResourceBundle jedisPorperties = ResourceBundle.getBundle("jedis");
maxtotal = Integer.valueOf(jedisPorperties.getString("maxtotal"));
maxwaitmillis = Integer.valueOf(jedisPorperties.getString("maxwaitmillis"));
port = Integer.valueOf(jedisPorperties.getString("port"));
host = jedisPorperties.getString("host");
}
//初始化连接池
static {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(maxtotal);
config.setMaxIdle(maxwaitmillis);
jedisPool = new JedisPool(config,host, port);
}
/**
* 获取jedis客户端操作对象
* @return
*/
public static Jedis getJedis() {
return jedisPool.getResource();
}
/**
* 释放资源
* @param jedis
*/
public static void close(Jedis jedis) {
if (null != jedis) {
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);
}
}
7.案例:Redis实战之查询所有学生
1、需求
index.html页面加载完成之后,使用ajax请求将所有的学员信息加载到页面的table中;
2、需求分析
实现如上需求有两种方案,分别如下:
-
页面加载完成之后,ajax发送异步请求从数据库查询数据,以后每次请求都从数据库中查询;
-
页面加载完成之后,ajax发送异步请求从数据库中查询数据后返回,并把数据存储在redis中,以后的查询直接从redis中获取;
由于,学生的信息在很长一段时间内是固定不变的,且数据量比较大。如果每次查询都从数据库查询,比较消消耗数据库性能,而且查询速度不快。所以,最佳方案是将数据查询出来后存储在redis中,后面的查询直接从redis中获取。
3、实现步骤
1、index.html页面加载完成之后,使用ajax技术向后台发送请求查询数据;
2、第一次查询时,直接从数据库中查询,查询到数据后将数据存储在redis中;
3、再次加载index.html页面时,请求发送到后台时,先判断redis中是否有数据,如果有则直接返回;如果没有,从数据库中查询;
4、index.html中拿到数据后,解析数据,将数据插入到页面的table中;
4、代码实现
1、前端页面:
【index.html】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="css/bootstrap.css">
</head>
<body>
<br><br><br><br><br>
<div class="text-center">
<a href="stus.html" class="btn btn-primary">学生列表</a>
</div>
</body>
</html>
【stu_list.html】
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>layui</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="css/bootstrap.css">
<!-- 注意:如果你直接复制所有代码到本地,上述css路径需要改成你本地的 -->
<style>
.stuTable{
width: 70%;
margin: 20px auto;
}
</style>
</head>
<body>
<table class="stuTable table table-bordered " id="stuTable" >
<caption class="text-center h2">学员列表</caption>
<tr>
<th>ID</th>
<th>姓名</th>
<th>学号</th>
<th>性别</th>
<th>班级</th>
<th>学科</th>
</tr>
</table>
<script src="js/jquery-3.3.1.js" charset="utf-8"></script>
</body>
</html>
2、后台环境搭建
2.1 数据库
【创建数据库】
CREATE TABLE `stu_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_name` varchar(255) DEFAULT NULL,
`student_no` varchar(255) DEFAULT NULL,
`sex` int(11) DEFAULT NULL COMMENT '1-男 2-女',
`class_id` int(11) DEFAULT NULL,
`test_id` int(11) DEFAULT NULL,
`subject_no` int(11) DEFAULT NULL COMMENT '学科编号:1-java 2-php 3-python 4-UI 5-前端 6-其他',
`password` varchar(255) DEFAULT NULL,
`test_status` int(11) DEFAULT NULL COMMENT '考试状态:0-结束 1-未结束',
`test_type` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7969 DEFAULT CHARSET=utf8;
【导入数据】
2.2 创建实体类Student.java
package com.heima.domain;
public class Student {
private Integer id;
private String student_name;
private String student_no;
private Integer sex;
private Integer class_id;
private Integer subject_no;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getStudent_name() {
return student_name;
}
public void setStudent_name(String student_name) {
this.student_name = student_name;
}
public String getStudent_no() {
return student_no;
}
public void setStudent_no(String student_no) {
this.student_no = student_no;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public Integer getClass_id() {
return class_id;
}
public void setClass_id(Integer class_id) {
this.class_id = class_id;
}
public Integer getSubject_no() {
return subject_no;
}
public void setSubject_no(Integer subject_no) {
this.subject_no = subject_no;
}
}
2.3 导入jar包
2.4 连接池和工具类
配置文件
【c3p0-config.xml】
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///stu_list?useUnicode=true&characterEncoding=UTF8</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">2</property>
<property name="initialPoolSize">5</property>
<property name="minPoolSize">1</property>
<property name="maxPoolSize">5</property>
</default-config>
</c3p0-config>
【jedis.properties】
maxtotal=100
maxwaitmillis=3000
host=127.0.0.1
port=6379
注意:以上两个配置文件放置到src目录下。
工具类
【JdbcTemplateUtils.java】
package com.heima.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
/**
* JdbcTemplate工具类
*/
public class JdbcTemplateUtils {
private static final DataSource ds = new ComboPooledDataSource();
/**
* 获取DataSources
* @return
*/
public static DataSource getDataSources() {
return ds;
}
}
【JedisUtil.java】
package com.heima.utils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ResourceBundle;
/**
* Jedis工具类
*/
public class JedisUtil {
private static JedisPool jedisPool;
private static int maxtotal;
private static int maxwaitmillis;
private static String host;
private static int port;
//加载配置文件
static {
ResourceBundle jedisPorperties = ResourceBundle.getBundle("jedis");
maxtotal = Integer.valueOf(jedisPorperties.getString("maxtotal"));
maxwaitmillis = Integer.valueOf(jedisPorperties.getString("maxwaitmillis"));
port = Integer.valueOf(jedisPorperties.getString("port"));
host = jedisPorperties.getString("host");
}
//初始化连接池
static {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(maxtotal);
config.setMaxIdle(maxwaitmillis);
jedisPool = new JedisPool(config,host, port);
}
/**
* 获取jedis客户端操作对象
* @return
*/
public static Jedis getJedis() {
return jedisPool.getResource();
}
/**
* 释放资源
* @param jedis
*/
public static void close(Jedis jedis) {
if (null != jedis) {
jedis.close();
}
}
}
3、代码实现
3.1 【stu_list.html】加载完成后发送ajax请求
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>layui</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="css/bootstrap.css">
<style>
.stuTable{
width: 70%;
margin: 20px auto;
}
</style>
</head>
<body>
<table class="stuTable table table-bordered " id="stuTable" >
<caption class="text-center h2">学员列表</caption>
<tr>
<th>ID</th>
<th>姓名</th>
<th>学号</th>
<th>性别</th>
<th>班级</th>
<th>学科</th>
</tr>
</table>
<script src="js/jquery-3.3.1.js" charset="utf-8"></script>
<script>
$.get("/getStuListServlet",function (stuData) {
//遍历数据
$(stuData).each(function (index, element) {
element.sex==1?element.sex="男":element.sex="女";
element.class_id = "就业班";
element.subject_no = "java";
$("<tr><td>"+element.id+"</td><td>"+element.student_name+"</td><td>"+element.student_no+"</td><td>"+element.sex+"</td><td>"+element.class_id+"</td><td>"+element.subject_no+"</td></tr>").appendTo($("#stuTable"));
})
});
</script>
</body>
</html>
【GetStuinfoServlet.java】 web层代码实现
package com.heima.servlet;
import com.heima.service.StudentService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/getStuListServlet")
public class GetStuinfoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//查询所有学员信息
StudentService studentService = new StudentService();
String stuJsonData = studentService.queryAllStudent();
//响应数据
resp.setContentType("text/json;charset=utf-8");
resp.getWriter().println(stuJsonData);
}
}
**【StudentService.java】**service层代码
package com.heima.service;
import com.alibaba.fastjson.JSON;
import com.heima.dao.StudentDao;
import com.heima.domain.Student;
import com.heima.utils.JedisUtil;
import redis.clients.jedis.Jedis;
import java.util.List;
public class StudentService {
StudentDao studentDao = new StudentDao();
/**
* 查询所有学员信息
* @return
*/
public String queryAllStudent() {
//先从redis中获取
Jedis jedis = JedisUtil.getJedis();
long redisBegin = System.currentTimeMillis();
String stusData = jedis.get("stusData");
if(null!=stusData){
long redisEnd = System.currentTimeMillis();
System.out.println("从redis中查询耗时:"+(redisEnd-redisBegin));
}
//从数据库中查询
if (null == stusData) {
long begin = System.currentTimeMillis();
List<Student> studentList = studentDao.queryAll();
long end = System.currentTimeMillis();
System.out.println("从数据库中查询耗时:"+(end-begin));
//将数据转化成json字符串
stusData = JSON.toJSONString(studentList);
//将数据存到redis中
jedis.set("stusData", stusData);
}
//释放资源
jedis.close();
return stusData;
}
}
【StudentDao.java】dao层代码
package com.heima.dao;
import com.heima.domain.Student;
import com.heima.utils.JdbcTemplateUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class StudentDao {
/**
* 查询所有数据
* @return
*/
public List<Student> queryAll() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcTemplateUtils.getDataSources());
String sql = "select * from stu_info";
List<Student> studentList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class));
return studentList;
}
}
测试:分别测试从数据库查询和从redis中查询消耗的时间;
附: 安装Redis到系统服务中
win+R==》运行:services.msc
打开redis的官方安装文档:RedisService.docx
文档中有如下安装服务和卸载服务的说明:
【安装服务】
cmd进入到Redis的安装目录下,输入以下命令:
redis-server --service-install redis.windows.conf --loglevel verbose
安装完成后,查看系统服务列表(win+r 输入:services.msc),找到redis后右击启动: