简介
Redis 是用C语言开发完全开源免费的,遵守BSD协议的,一个高性能的,key-value型的,NOSQL数据库。
特点
- 可以将内存中的数据持久化到硬盘中,重启的时候可以从硬盘中再次加载
- 拥有丰富的数据类型,String、Hash、List、Set、ZSet(SortedSet)是常用的数据类型
- 极高的读写性能
- 拥有高可用架构,支持集群部署
安装
- Github下载地址:https://github.com/tporadowski/redis/releases
- 中文官网下载地址:http://www.redis.cn/
- github上面下载下来的压缩包默认是包含了Redis服务端程序和一个客户端访问程序的,redis-server.exe为Redis的服务端,redis-cli.exe为Redis的客户端。在redis-cli中可以用redis特有的语法或者说命令来和服务端交互,实现数据的CURD等操作,实际开发过程中,则是使用具体的工具类或者框架来和redis的服务端交互,例如java使用Jedis来和Redis交互。
- 首先启动server,再启动cli,这时候cli端会自动连接上服务端,尝试在cli中执行一些命令,config get *(查看Redis详细配置信息的命令)
可以看到配置信息已经在cli中打印出来了,dbfilename配置为数据库文件的名称,默认是dump.rdb,requirepass配置为数据库的访问密码,可以看到当前为空,即无需密码就可以访问
数据类型
- String
String是Redis中最基本的数据类型
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象
string类型是Redis最基本的数据类型,一个键最大能存储512MB。
单值单Value
- SET key1 beijing : 添加一条数据,SET为关键字,key1 是键 ,beijing 是值
- keys * :查询所有的键 , redis关键字不区分大小写
- GET key1 :根据键查找值
- string 的其他命令
- del :根据键删除
- append :追加
- strlen :根据键查询值的长度
- incr :Increment,值在当前基础上加1,仅对数字有效,可用于高并发下的秒杀活动
- decr :DeleteCreate,值在当前基础上减1,仅对数字有效
- setex : SetWithExpire,添加一条数据,并指定多久过期
- setnx : SetIfNotExist,添加一条数据,若指定的键已经存在就添加失败,若不存在添加成功
- mset :MoreSet,批量添加,类似的还有mget(批量根据建获取值),msetnx(批量添加,若指定的某个键已经存在,全部添加失败,否则全部添加成功)
- Hash
hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。Redis 中每个 hash 可以存储 2 的 32 次方减 1 个键值对(40多亿)
KV模式不变,但V是一个键值对。有点像java中的Map
- hset user id 11 : 添加一条记录,hset为关键字, user 为键,id 11 为值,此时的值就是一个键值对结构了
- hget user id:根据键和【值中的键】查询一条记录
- hmset customer id 11 name xiaoming age 20:批量添加数据,此时的键为customer,值是id 11 name xiaoming age 20,但同时整个值呈现的是KV的结构
- hmget customer id name age:批量根据键+值中的键查询值中的值
- hgetall customer:根据键获取值
- hash的其他命令:
- hlen:根据键查询值的数量
- hexists key :在键里面的某个值得键是否存在
- hkeys/hvals:根据键获取值中所有的键/所有的值
- hincrby/hincrbyfloat:定量增加整数,定量增加小数
- hsetnx:添加时候,判断是否已经存在
- List
Redis列表是简单的字符串列表,按照插入顺序排序。可以从头部(左边)或者尾部(右边)进行插入。一个列表最多可以包含 2 的 32 次方 - 1 个元素 (4294967295, 每个列表可容纳超过40亿个元素)。
单值多value
- lpush list01 1 2 3 4 5:LeftPush,创建一个键为list01的列表,并有序的从左边添加 1 2 3 4 5 这几个元素
- lrange list01 0 -1:从左边查询键为list01的列表
- rpush list02 1 2 3 4 5:RightPush,创建一个键为list02的列表,并有序的从右边添加 1 2 3 4 5 这几个元素
- lpop list01:从list01列表的左边弹出一个元素
- lindex list01 3:按照下标访问列表元素
- list的其他命令:
- llen:求长度
- lrem key :删除N个value
- ltrlm key 开始index 结束index :截取指定范围的值后再赋值给key
- Set
Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的时间复杂度都是O(1)。集合中最大的成员数为 2 的 32 次方 - 1 (4294967295, 每个集合可存储40多亿个成员)。
单值多value
- sadd set01 1 1 2 2 3 3:创建一个键为set01的集合,重复的值会被覆盖。
- smembers set01:根据键查询值
- sismember set01 3:判断set01集合中是否存在值为3的元素
- Set的其他命令:
- scard:获取集合里面元素的个数
- srem key value :根据键删除值中的某个元素
- srandmember key 3 :随机获取集合中的3个元素,可用于高并发场景下的抽奖
- spop key :随机出栈
C#客户端
StackExchange.Redis:StackOverflow开源的一个Redis(C#)客户端
Microsoft.Extensions.Caching.Redis:微软官方基于StackExchange.Redis 封装后的Redis客户端
下面用代码演示基于Microsoft.Extensions.Caching.Redis和Redis-Server进行交互
-
新建控制台应用程序,项目属性如下所示
-
项目目录结构和nuget依赖
-
RedisUtil为redis交互工具类,封装了Redis常用数据类型的存储和读取操作。具体代码如下
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RedisDemo.Utils
{
class RedisUtil
{
public static readonly Lazy<RedisUtil> lazy = new Lazy<RedisUtil>(() => new RedisUtil());
public static RedisUtil redis { get { return lazy.Value; } }
private RedisUtil() { }
private static IDatabase db = null;
static RedisUtil()
{
ConnectionMultiplexer conn = ConnectionMultiplexer.Connect("127.0.0.1:6379");
db = conn.GetDatabase();
}
/// <summary>
/// String类型set
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public static bool StringSet(string key, string value)
{
return db.StringSet(key, value);
}
/// <summary>
/// String类型set
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="timeSpan">过期时间</param>
/// <returns></returns>
public static bool StringSet(string key, string value, TimeSpan timeSpan)
{
return db.StringSet(key, value, timeSpan);
}
/// <summary>
/// String类型get
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string StringGet(string key)
{
return db.StringGet(key);
}
/// <summary>
/// Hash类型set
/// </summary>
/// <typeparam name="V"></typeparam>
/// <param name="key"></param>
/// <param name="dic"></param>
public static void HashSet<V>(string key, Dictionary<string, V> dic)
{
if (dic == null || dic.Count <= 0) { return; }
HashEntry[] entries = new HashEntry[dic.Count];
List<string> keys = new List<string>(dic.Keys);
for (int i = 0; i < dic.Count; i++)
{
string value = JsonConvert.SerializeObject(dic[keys[i]]);
entries[i] = new HashEntry(keys[i], value);
}
db.HashSet(key, entries);
}
/// <summary>
/// Hash类型set
/// </summary>
/// <typeparam name="V"></typeparam>
/// <param name="key"></param>
/// <param name="field"></param>
/// <param name="value"></param>
/// <returns></returns>
public static bool HashSet<V>(string key, string field, V value)
{
return db.HashSet(key, field, JsonConvert.SerializeObject(value));
}
/// <summary>
/// Hash类型get
/// </summary>
/// <typeparam name="V"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public static Dictionary<string, V> HashGet<V>(string key)
{
HashEntry[] entries = db.HashGetAll(key);
if (entries != null && entries.Length > 0)
{
Dictionary<string, V> dics = new Dictionary<string, V>();
foreach (var item in entries)
{
dics.Add(item.Name, JsonConvert.DeserializeObject<V>(item.Value));
}
return dics;
}
return null;
}
/// <summary>
/// Hash类型get
/// </summary>
/// <param name="key"></param>
/// <param name="field"></param>
/// <returns></returns>
public static string HashGet(string key, string field)
{
return db.HashGet(key, field);
}
/// <summary>
/// List添加的时候只能一个一个的添加
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="values"></param>
/// <returns></returns>
public static bool ListSet<T>(string key, List<T> values)
{
if (values == null | values.Count <= 0) { return false; }
for (int i = 0; i < values.Count; i++)
{
db.ListLeftPush(key, JsonConvert.SerializeObject(values[i]));
}
return true;
}
public static List<T> ListGet<T>(string key)
{
List<T> list = new List<T>();
RedisValue[] values = db.ListRange(key, 0, -1);
foreach (var item in values)
{
list.Add((T)JsonConvert.DeserializeObject(item));
}
return list;
}
public static bool SetSet<T>(string key, List<T> values)
{
if (values == null | values.Count <= 0) { return false; }
for (int i = 0; i < values.Count; i++)
{
db.SetAdd(key, JsonConvert.SerializeObject(values[i]));
}
return true;
}
public static List<T> SetGet<T>(string key)
{
List<T> list = new List<T>();
RedisValue[] values = db.SetMembers(key);
foreach (var item in values)
{
list.Add((T)JsonConvert.DeserializeObject(item));
}
return list;
}
}
}
- Main 方法中编写测试代码
using Newtonsoft.Json;
using RedisDemo.Models;
using RedisDemo.Utils;
using System;
using System.Collections.Generic;
namespace RedisDemo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
//string
bool isok = RedisUtil.StringSet("age", "20", new TimeSpan(1, 0, 0));
Console.WriteLine(isok);
string result = RedisUtil.StringGet("age");
Console.WriteLine(result);
//Hash
//基本数据类型
//Dictionary<string, string> dic01 = new Dictionary<string, string>();
//dic01.Add("dic-key01", "dic-v1");
//dic01.Add("dic-key02", "dic-v2");
//RedisUtil.HashSet("dic01", dic01);
//Dictionary<string, string> result = RedisUtil.HashGet<string>("dic01");
//Console.WriteLine(JsonConvert.SerializeObject(result));
//dic的value为一个实体类
//Dictionary<string, Sku> dic02 = new Dictionary<string, Sku>();
//dic02.Add("dic-key01", new Sku() { id = 1, name = "复印纸", price = 10.00M });
//dic02.Add("dic-key02", new Sku() { id = 2, name = "A5复印纸", price = 100.00M });
//RedisUtil.HashSet("dic02", dic02);
//Dictionary<string, Sku> result = RedisUtil.HashGet<Sku>("dic02");
//Console.WriteLine(JsonConvert.SerializeObject(result));
//dic的value为一个嵌套实体类
//Dictionary<string, Spu> dic03 = new Dictionary<string, Spu>();
//dic03.Add("k1", new Spu() { id = 1, name = "复印纸", skus = new List<Sku>() { new Sku() { id = 1, name = "A3复印纸", price = 1.00M } } });
//dic03.Add("k2", new Spu() { id = 2, name = "打印纸", skus = new List<Sku>() { new Sku() { id = 1, name = "A3打印纸", price = 91.00M } } });
//RedisUtil.HashSet("dic03", dic03);
//Dictionary<string, Spu> result = RedisUtil.HashGet<Spu>("dic03");
//Console.WriteLine(JsonConvert.SerializeObject(result));
//List
//List<int> lists = new List<int>() { 1, 2, 3, 4, 5 };
//bool isok = RedisUtil.ListSet("list02", lists);
//List<long> result = RedisUtil.ListGet<long>("list02");
//Console.WriteLine(JsonConvert.SerializeObject(result));
//Set
//List<int> lists = new List<int>() { 1, 2, 3, 4, 5,5 };
//bool isok = RedisUtil.SetSet("set01", lists);
//List<long> result = RedisUtil.SetGet<long>("set01");
//Console.WriteLine(JsonConvert.SerializeObject(result));
}
}
}
- String类型存储和读取
bool isok = RedisUtil.StringSet("age", "20", new TimeSpan(1, 0, 0));
Console.WriteLine(isok);
string result = RedisUtil.StringGet("age");
Console.WriteLine(result);
TimeSpan为设置的过期时间,三个参数分别表示时,分,秒
输出结果过为
Hello World!
True
20
- Hash存储一个基本数据类型的字典集合
Dictionary<string, string> dic01 = new Dictionary<string, string>();
dic01.Add("dic-key01", "dic-v1");
dic01.Add("dic-key02", "dic-v2");
RedisUtil.HashSet("dic01", dic01);
Dictionary<string, string> result = RedisUtil.HashGet<string>("dic01");
Console.WriteLine(JsonConvert.SerializeObject(result));
输出结果为
Hello World!
{"dic-key01":"dic-v1","dic-key02":"dic-v2"}
- Hash存储一个value为实体类的字典
Dictionary<string, Sku> dic02 = new Dictionary<string, Sku>();
dic02.Add("dic-key01", new Sku() { id = 1, name = "复印纸", price = 10.00M });
dic02.Add("dic-key02", new Sku() { id = 2, name = "A5复印纸", price = 100.00M });
RedisUtil.HashSet("dic02", dic02);
Dictionary<string, Sku> result = RedisUtil.HashGet<Sku>("dic02");
Console.WriteLine(JsonConvert.SerializeObject(result));
输出结果为
Hello World!
{"dic-key01":{"id":1,"name":"复印纸","price":10.00},"dic-key02":{"id":2,"name":"A5复印纸","price":100.00}}
- Hash存储一个value为嵌套的实体类的字典
Dictionary<string, Spu> dic03 = new Dictionary<string, Spu>();
dic03.Add("k1", new Spu() { id = 1, name = "复印纸", skus = new List<Sku>() { new Sku() { id = 1, name = "A3复印纸", price = 1.00M } } });
dic03.Add("k2", new Spu() { id = 2, name = "打印纸", skus = new List<Sku>() { new Sku() { id = 1, name = "A3打印纸", price = 91.00M } } });
RedisUtil.HashSet("dic03", dic03);
Dictionary<string, Spu> result = RedisUtil.HashGet<Spu>("dic03");
Console.WriteLine(JsonConvert.SerializeObject(result));
输出结果为
Hello World!
{"k1":{"id":1,"name":"复印纸","skus":[{"id":1,"name":"A3复印纸","price":1.00}]},"k2":{"id":2,"name":"打印纸","skus":[{"id":1,"name":"A3打印纸","price":91.00}]}}
- List存储一个int类型的List集合,这里存的时候实际是int32,但是取出来的时候是int64,所以使用了long来存方取出的数据
List<int> lists = new List<int>() { 1, 2, 3, 4, 5 };
bool isok = RedisUtil.ListSet("list02", lists);
List<long> result = RedisUtil.ListGet<long>("list02");
Console.WriteLine(JsonConvert.SerializeObject(result));
输出结果为
Hello World!
[5,4,3,2,1]
- Set存储一个int集合,从输出结果可以看到,重复的那个5没有存进去,redis的set数据类型是无序不可重复的
List<int> lists = new List<int>() { 1, 2, 3, 4, 5, 5 };
bool isok = RedisUtil.SetSet("set01", lists);
List<long> result = RedisUtil.SetGet<long>("set01");
Console.WriteLine(JsonConvert.SerializeObject(result));
输出结果为
Hello World!
[1,2,3,4,5]
spring-boot客户端
- 关于java客户端就以spring-boot做为切入点简单演示
- org.springframework.data.redis.core 只是封装了常用的接口,具体实现还是通过jedis或者lettuce去和redis-server交互
- spring-boot 2.0之前默认的redis客户端是jedis;之后默认的是lettuce
-
新建spring-boot的web应用,采用maven来管理依赖,项目文件结构如下:
-
在pom.xml中增加如下dependency。
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
- 修改application.properties。配置redis-server的访问地址
server.port=8081
spring.redis.host=127.0.0.1
spring.redis.port=6379
- 新建一个RestController,并命名为TestRedisController
@RestController
@RequestMapping("/redis/")
public class TestRedisController {}
- 注入RedisTemplate对象,此对象为springframework框架提供的一个对象,通过此对象实现spring-boot和redis-server的交互
@Autowired
private RedisTemplate<String, String> redisTemplate;
- 相关示例
/**
* String类型set
*
* @param key
* @param value
* @return
*/
@GetMapping("string/set/{key}/{value}")
public boolean stringSet(@PathVariable("key") String key, @PathVariable("value") String value) {
redisTemplate.opsForValue().set(key, value);
return true;
}
/**
* String类型get
*
* @param key
* @return
*/
@GetMapping("string/get/{key}")
public String stringGet(@PathVariable("key") String key) {
return redisTemplate.opsForValue().get(key);
}
/**
* Hash类型ser
*
* @param key
* @return
*/
@GetMapping("hash/set/{key}")
public boolean hashSet(@PathVariable("key") Integer key) {
UserDto userDto = new UserDto().setId(key).setName("redis").setStature(0.55D);
Map<String, String> map = new Hashtable<>();
map.put(key.toString(), JSON.toJSONString(userDto));
redisTemplate.opsForHash().putAll(key.toString(), map);
return true;
}
/**
* Hash类型ser
*
* @param key
* @param field
* @param value
* @return
*/
@GetMapping("hash/set/{key}/{field}/{value}")
public boolean hashSet(@PathVariable("key") String key, @PathVariable("field") String field, @PathVariable("value") String value) {
redisTemplate.opsForHash().put(key, field, value);
return true;
}
/**
* Hash类型get
*
* @param key
* @return
*/
@GetMapping("hash/get/{key}")
public UserDto hashGet(@PathVariable("key") String key) {
String result = JSON.toJSONString(redisTemplate.opsForHash().get(key, key));
return JSON.parseObject(result, UserDto.class);
}
/**
* Hash类型get
*
* @param key
* @param field
* @return
*/
@GetMapping("hash/get/{key}/{field}")
public String hashGet(@PathVariable("key") String key, @PathVariable("field") String field) {
return JSON.toJSONString(redisTemplate.opsForHash().get(key, field));
}
/**
* List类型set - 一个一个的增加
*
* @param key
* @param value
* @return
*/
@GetMapping("list/set/{key}/{value}")
public boolean listSet(@PathVariable("key") String key, @PathVariable("value") String value) {
String[] array = value.split(",");
for (String item : array) {
redisTemplate.opsForList().leftPush(key, item);
}
return true;
}
/**
* List类型get
*
* @param key
* @return
*/
@GetMapping("list/get/{key}")
public List<String> listGet(@PathVariable("key") String key) {
return redisTemplate.opsForList().range(key, 0, -1);
}
/**
* Set类型set
*
* @param key
* @param value
* @return
*/
@GetMapping("set/set/{key}/{value}")
public boolean setSet(@PathVariable("key") String key, @PathVariable("value") String value) {
String[] array = value.split(",");
for (String item : array) {
redisTemplate.opsForSet().add(key, item);
}
return true;
}
/**
* Set类型get
*
* @param key
* @return
*/
@GetMapping("set/get/{key}")
public Set<String> setGet(@PathVariable("key") String key) {
return redisTemplate.opsForSet().members(key);
}
常用命令
- del key:删除某一个key,对应的value也就删除了
- flushdb :清空当前数据库,删除所有数据
引用
- 官网:https://www.redis.net.cn/
- redis客户端工具:https://zhuanlan.zhihu.com/p/86337312
- B站视频教程:https://www.bilibili.com/video/BV1EJ411g7Dy