Redis 高性能数据库,Redis能轻松实现秒杀系统

Redis 写入内存而不是写入硬盘、异步处理而不是同步处理、分布式处理

安装 Redis

官方建议在 linux 上安装

本次使用windows安装  Redis Windows版本 下载地址

> redis-cli  连接redis客户端
Microsoft Windows [版本 10.0.19044.1526]
(c) Microsoft Corporation。保留所有权利。

C:\Users\CodeHaywire>redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>

Ridis的数据类型

string(字符串) hash(哈希) list(列表) set(集合) zset(sorted set:有序集合)

#字符串
Microsoft Windows [版本 10.0.19044.1526]
(c) Microsoft Corporation。保留所有权利。

C:\Users\CodeHaywire>redis-cli
127.0.0.1:6379> set name zhanshan
OK
127.0.0.1:6379> get name
"zhanshan"
127.0.0.1:6379>

mset s1 "hello" s2 "world" s3 "ok" 同时设置多个 mget:同时获得多个

127.0.0.1:6379> mset s1 "hello" s2 "world" s3 "ok"
OK
127.0.0.1:6379> mget s1 s2
1) "hello"
2) "world"
127.0.0.1:6379>

数据加减 incrby :添加 decrby:减少 使用场景:计数器

127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> get num
"100"
127.0.0.1:6379> incrby num 100
(integer) 200
127.0.0.1:6379> get num
"200"
127.0.0.1:6379>

keys * 查询所有键

127.0.0.1:6379> keys *
1) "name"
2) "s1"
3) "num"
4) "s2"
5) "s3"
127.0.0.1:6379>

del key 删除某个键 type key 查看某个键的类型 rename key newkey 重命名某个键

定时删除 expire key 10 10s后删除该键

127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> get name
"zhanshan"
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379>

strlen key 查询字符串的长度

127.0.0.1:6379> strlen s1
(integer) 5
127.0.0.1:6379> get s1
"hello"
127.0.0.1:6379>

list 列表

lpush list value1 value2 value3 头部添加
rpush list value1 value2 value3 尾部添加
lrange book start end 查询全部数据
lpop key 移除头部第一个并返回删除的值
rpop key 移除尾部第一个并返回删除的值

127.0.0.1:6379> lpush book c++ c# c
(integer) 5
127.0.0.1:6379> lrange book 0 5  #查询全部数据
1) "c"
2) "c#"
3) "c++"
4) "c#"
5) "c++"
127.0.0.1:6379> lrange book 0 -1
1) "c"
2) "c#"
3) "c++"
4) "c#"
5) "c++"
127.0.0.1:6379> rpush book java
(integer) 6
127.0.0.1:6379> lrange book 0 -1
1) "c"
2) "c#"
3) "c++"
4) "c#"
5) "c++"
6) "java"
127.0.0.1:6379>
127.0.0.1:6379> lrange book 0 -1
1) "c"
2) "c#"
3) "c++"
4) "c#"
5) "c++"
6) "java"
127.0.0.1:6379> lpop book  #移除头部第一个并返回删除的值 #rpop相反
"c"
127.0.0.1:6379> lrange book 0 -1
1) "c#"
2) "c++"
3) "c#"
4) "c++"
5) "java"
127.0.0.1:6379>

llen key 查询list的长度

127.0.0.1:6379> llen book  #查询list的长度
(integer) 5
127.0.0.1:6379> lrange book 0 5
1) "c#"
2) "c++"
3) "c#"
4) "c++"
5) "java"
127.0.0.1:6379>

lrem key count value 删除指定元素 解释 count

127.0.0.1:6379> lrange book 0 5
1) "c#"
2) "c++"
3) "c#"
4) "c++"
5) "java"
127.0.0.1:6379> lrem book 2 c#
(integer) 2
127.0.0.1:6379> lrange book 0 5
1) "c++"
2) "c++"
3) "java"
127.0.0.1:6379>

集合 set

sadd key value 添加数据
srem key value 移除元素
smembers key value查询所有元素
scard key 打印集合大小
sismember key value 查询给定元素是否在集合中
sunion key key 打印集合的并集
sdiff key key 打印集合的交集

哈希 hash

hset hash key value 哈希表里插入数据
hget hash key 获得哈希表里数据
hexists hash key 查询某个数据是否存在
hlen hash 打印哈希表的长度

127.0.0.1:6379> hset student stuName zhanshan
(integer) 1
127.0.0.1:6379> hset student age 12
(integer) 1
127.0.0.1:6379> hget student stuNname
(nil)
127.0.0.1:6379> hget student stuName
"zhanshan"
127.0.0.1:6379> hget student age
"12"
127.0.0.1:6379> hexists student age
(integer) 1
127.0.0.1:6379> hexists student qq
(integer) 0
127.0.0.1:6379>

有序集合 zset

zadd set key value 哈希表里插入数据
zrem set key value 哈希表里删除数据

使用场景:计数器 缓存数据  更多使用场景

mysql redis mongodb 三者如何选择

flushall 清空所有数据

Java 连接 redis

    <dependencies>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.8.0</version>
        </dependency>
    </dependencies>

Java 连接 redis 使用文档

package cn.js.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**
 * @ClassName : ConnectRedis
 * @Description : 连接redis
 * @Author : CodeHaywire
 * @Created at 2022/3/28 8:35
 */
public class ConnectRedis {
    public static void main(String[] args) {
        JedisPool pool = new JedisPool("localhost", 6379);
        try (Jedis jedis = pool.getResource()) {
            //添加数据
            jedis.set("s1", "Jedis");
        }
    }
}

打开 cmd

127.0.0.1:6379> keys *
1) "s2"
2) "db"
3) "s3"
4) "student"
5) "s1"
6) "book"
7) "num"
127.0.0.1:6379> get s1
"Jedis"
127.0.0.1:6379>
package cn.js.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**
 * @ClassName : ConnectRedis
 * @Description : 连接redis
 * @Author : CodeHaywire
 * @Created at 2022/3/28 8:35
 */
public class ConnectRedis {
    public static void main(String[] args) {
        JedisPool pool = new JedisPool("localhost", 6379);
        try (Jedis jedis = pool.getResource()) {
            //添加数据
            jedis.set("s1", "Jedis");
            //获得数据
            String s1 = jedis.get("s1");
            System.out.println(s1);
        }
    }
}
package cn.js.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**
 * @ClassName : ConnectRedis
 * @Description : 连接redis
 * @Author : CodeHaywire
 * @Created at 2022/3/28 8:35
 */
public class ConnectRedis {
    public static void main(String[] args) {
        JedisPool pool = new JedisPool("localhost", 6379);
        try (Jedis jedis = pool.getResource()) {
            Long length = jedis.strlen("s1");
            System.out.println(length);
        }
    }
}
···
5
···
//  Long count = jedis.del("s1");
//  System.out.println(count);
···
1
···
Set<String> keys = jedis.keys("*");
	for (String key : keys) {
		System.out.println(jedis.get(key));
	}
···
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
redis
100   
···
package cn.js.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

/**
 * @ClassName : ConnectRedis
 * @Description : 使用方法大同小异
 * @Author : CodeHaywire
 * @Created at 2022/3/28 8:35
 */
public class ConnectRedis {
    public static void main(String[] args) {
        JedisPool pool = new JedisPool("localhost", 6379);
        try (Jedis jedis = pool.getResource()) {
            jedis.lpush("book", "Java", "C++", "vue");
            jedis.rpush("book", "JavaScript", "C#", "Go");
            List<String> book = jedis.lrange("book", 0, -1);
            for (String s : book) {
                System.out.println(s);
            }
        }
    }
}
···
127.0.0.1:6379> LRANGE book 0 -1
1) "vue"
2) "C++"
3) "Java"
4) "JavaScript"
5) "C#"
6) "Go"
127.0.0.1:6379>
···

redis事务

redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令

相关命令 : MULTI EXEC DISCARD WATCH UNWARCH

multi : 标记一个事务块的开始( queued )
exec : 执行所有事务块的命令 (一旦执行exec后,之前加的监控锁都会被取消掉)
discard : 取消事务,放弃事务块中的所有命令
watch : 对 key 监控
unwatch : 取消watch对所有key的监控

更多内容 参考 Deep In Thought 的 博客

小示例:redis事务操作图

redis 不支持回滚,出现错误时,不会回滚,跳过这条命令,仍然执行其他命令

watch机制+事务 阻止超卖

模拟A账户购买网卡未及时支付,晚一步支付

127.0.0.1:6379> set account:a 1000
OK
127.0.0.1:6379> set product:num 1
OK
127.0.0.1:6379> watch account:a product:num
OK
127.0.0.1:6379> get account:a
"1000"
127.0.0.1:6379> get product:num
"1"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby account:a 1000
QUEUED
127.0.0.1:6379> decrby product:num 1
QUEUED
127.0.0.1:6379>

模拟B账户购买成功

C:\Users\CodeHaywire>redis-cli
127.0.0.1:6379> get account:b
(nil)
127.0.0.1:6379> set account:b 1000
OK
127.0.0.1:6379> watch account:b product:num
OK
127.0.0.1:6379> get account:b
"1000"
127.0.0.1:6379> get product:num
"1"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> decrby account:b 1000
QUEUED
127.0.0.1:6379> decrby product:num 1
QUEUED
127.0.0.1:6379> exec
1) (integer) 0
2) (integer) 0
127.0.0.1:6379>

A网好了后去买

127.0.0.1:6379> exec  
(nil) 没了
127.0.0.1:6379> get account:a 
"1000" 钱还在
127.0.0.1:6379>

数据如何存?

mysql 中存 all 即所有的数据(redis只是缓存mysql中的部分数据),redis中缓存mysql中存在的访问量超级大的数据

如果redis中没有我要的数据,那么其实这些请求并发量没有那么大,那么就去mysql访问,肯定并没有太大压力

Java抢购的实现