- 基本介绍
- 为什么会用redi, 为什么大小公司都在用redis
- 微信红包
- 微博
- 淘宝
- 讲到redis不得不将nosql
- nosql是不同于传统的关系型数据库管理系统的统称, 其两者最重要的区别是nosql不使用sql作为查询语句
- noql数据库可以不需要固定的表格模式, noql是基于键值对, 可以想象成表中的主键和值的对应关系
- nosql: redis, memcached, mongodb, guava(loadingCache)
- redis定义
- redishi一个开源(BSD许可)的, 内存中的数据结构存储系统, 他可以用作数据库, 缓存和消息中间件, 它支持多种类型的数据结构, 如字符串(strings), 数列(hashes), 列表(lists),集合(sets),有序集合(sorted sets) 等
- 从大家熟悉的mysql出发来认识redis
- 概念: 关系型数据库的一个长江用法是存储长期的报告数据, 并将这些报告数据用作固定时间范围内的聚合数据, 收集聚合数据的常见做法是: 先将各个行插入一个报告表里面, 之后再通过扫描这些行来收集聚合数据
- 图解剖析mysql的执行过程(网上自己找图看)
- 分析redis和mencached和mysql之间的区别
- 从数据库类型, 数据存储方式, 特殊功能讲解redis和mencached和mysql的区别
- 作为同款功能的内存缓存产品, redis和memcached各有什么优势
- 内存管理机制
- mencached默认使用Slab Allocation机制管理内存, 其主要思想按照预先规定的大小, 将分配的内存分割成特定长度的块, 以存储相应长度的key-value数据记录, 以完全解决内容碎片问题,
- redis使用现场申请内存的方式来存储数据, 并且很少使用free-list等方式来优化内存分配, 会在一定程度上存在内存碎片
- 数据持久化方案
- memcached不支持内存数据的持久化操作, 所有的数据都以in-memory的形式存储
- redis支持持久化操作, redi提供了两种不同的持久化方法来将数据存储到硬盘里面
- 第一种是rdb形式: 基于全量护具备份, 备份的是数据
- 第二种是aof形式: append only if, 增量持久化备份, 备份的是指令 [例如: set key, del key]
- 缓存数据过期机制
- mencached在删除失效主键时也是采用消极方法, 即memcached内部也不会见识主键是否失败, 而是在通过get访问主键时才会检查其是否已经失败
- 定时, 定期等多种缓存失效机制, 减少内存泄露
- redis作为数据库和作为内存缓存的两种使用方法
- redis作为数据库的使用有什么优缺点:
- 优点: 没有Schema约束, 数据结构的变更相对容易, 抗压能力强, 性能极高
- 缺点: 没有索引, 没有外键, 缺少int/date等基本数据类型, 多条件查询需要通过集合内联(sinter, zintertore)和链接间接开发效率低, 可维护性不佳
- redis作为缓存的左右, 搭配数据库使用的两种方法
- jedis整合使用方案
- 第一层在缓存进行查询, 如果得到数据则直接返回
- 第二层在数据库进行查询, 并且刷新缓存, 方便下次查询
- 作为mybatis/hibernate二级缓存使用方法
- 一级缓存: sqlSession级别, 进程缓存, 单次链接有效
- 图解分析加redis前后的架构区别
掌握redis五中数据结构(string,list,set,sortset,hash)
- String: 是最常用的一种数据类型, 普通的key/value存储都可以归为此类
- set/get
- 设置key对应的值为string类型的value
- 获取key对应的值
- mget
- 批量获取多个key的值, 如果可以不存在则返回null
- incr && incrby
- incr对key对应的值进行加加操作, 并返回新的值; incrby加指定值
- decr && decrby
- decr对key对应的值进行减减操作, 并返回新的值; decrby减指定值
- sentx
- 设置key对应的值为String类型的value, 如果key直径存在则返回0
- setex
- 设置key对应的值为string类型的value, 并设定有效时间
- 其他指令
- getrange: 获取key对应value的子字符串
- mset: 数量设置多个key的值, 如果成功表示所有值都被设置, 否则返回0表示没有任何值被设置
- msetnx, 同mset, 不存在就设置, 不会覆盖已有的key
- getset: 设置key的值, 并返回key旧的值
- append: 给指定key的value增加字符串, 并返回新字符串的长度
Hash类型
- Hash类似于HashMap
- key / value的形式
- 格式: Map>
- 第一个String 就是 Map 的名称
- Hash 是一个String类型的field和value之间的映射类
- redis的Hash数据类型的key(hash表名称)对应的value实际的内部存储结构为一个HashHashMap
- Hash特别适合存储对象
- 相当于把一个对象的每个属性存储String类型, 将整个对象存储在Hash类型中会占用更少内存.
- 所存储的成员较少时数据存储为zipmap, 当成员数量大时会自动转成真正的HashMap, 此时encoding为ht
- 运用场景:
- 如用一个对象来存储对象信息, 商品信息, 订单信息等等
- Hash命令讲解:
- hset: 设置key对应的HashMap中的field的value
- hget: 获取key对应的HashMap中的field的value
- hgetall: 获取所有key-value信息
- hmset: 一次设置多个属性
- hdel: 删除一个属性
- hlen: 获取set的长度
LIst数据类型
- lpush: 在key对应的list的头部添加一个元素
- lrange: 获取key对应的list的指定下标范围的元素, -1表示获取所有元素
- lpop: 从key对应的list的头部删除一个元素, 并返回该元素
- rpush: 在key对应的list的尾部添加一个元素
- rpop: 从key对应的list的尾部删除一个元素, 并返回该元素
- lrem: 删除一定个数或者指定的元素
- lrem list 2 value2 删除两个值为value2的记录
- lindex: 获取指定索引下标的值
- llen: 获取list的长度
set集合
- sadd: 在key对应的set中添加一个元素
- smembers: 获取key对应的set的所有元素
- spop: 随机返回并删除key对应的set中的一个元素
- suion: 求给定key对应的set并集
- sinter: 求给定key对应的set交集
- sdiff: 求给定key对应的set的差集
SortSet讲解
- zadd: 在key对应的zset中添加一个元素
- zrange: 获取key对应的zset中指定范围的元素, -1表示获取所有元素
- zrem: 删除key对应的zset中的一个元素
- zrangebyscore: 返回有序集key中, 指定分户范围的元素列表
- zrank: 返回key对应的zet中指定member的排名, 其中member按score值递增(从小到大), 排名以0为底, 也就是说, score值是最小的成员排名为0
- zcard: 查询sortset有多少个元素(长度)
- set是通过hasmmap存储, key对应et的元素, value是空对象
- sortset是怎么存储并实现排序的呢, hashmap存储, 还加了一层跳跃表
- 跳跃表: 相当于双向链表, 在其基础上添加前往比当前元素大的跳跃链接
redis发布订阅
- 发布订阅作用: 类似于信息管道, 用来进行系统之间消息解耦, 类似于mq, rabbitmq, rocketmq, kafka, activemq, 主要有消息发布者和消息订阅者
- PUBLISH: 将消息message发送到指定的平道channel, 返回收到消息的客户端信息
- SUBSCRIBE: 订阅给指定频道的信息
- UNSUBSCRIBE: 取消订阅指定的频道, 如果不指定, 则取消订阅所有的频道
传统关系型数据库事务
- 一个数据库事务通常包含了一个序列的对数据库的读写操作, 他的存在包含有以下两个目的
- 为数据库操作序列提供了一个从失联中恢复到正常状态的方法, 同时提供了数据库即使在异常状态下仍然能保持一致性的方法
- 当多个应用程序在并发访问数据库时, 可以在这些应用程序之间提供一个隔离方法, 以防止彼此的操作互相干扰
- 事务ACID四大特性
- 原子性(Atomicity): 事务作为一个整体被执行, 包含在其中的对数据库的操作要么全部成功, 要么全部不成功
- 一致性(Consistency): 事务应该确保数据库的状态从一个状态转变为另一个状态一致
- 隔离性(Isolation): 多个事务并发执行时, 一个事务的执行不会影响另外一个事务
- 持久性(Durability): 已经被提交的事务对数据的修改应该是永久保存在数据库中
- 事务隔离机制:
- read uncommitted 读未提交
- read committed 读已提交
- repeatable read 可重复读
- serializable 串行化
redis事务机制
- MULTI 与 EXEX命令
- 以MULTI开始一个事务, 然后将多个命令如队列到事务中, 最后由EXEC命令触发事务, 一并执行十五中的所有命令
- DISCARD命令
- DISCARD命令用于取消一个事务, 他清空客户端整个事务队列, 然后将客户端从事务状态调整回非事务状态, 最后返回字符串OK给客户端, 说明事务已经被取消
- WATCH命令
- WATCH命令用于在事务开始之前见识任意数量的键. 当调用EXEC命令执行事务时, 如果任意一个被见识的键已经被其他客户端修改了, 那么整个事务不再执行, 直接返回失败.
- 图解redis执行事务过程原理
redis事务与传统关系型事务的比较
- 原子性(Automicity)
- 单个Redis命令的执行是原子性的, 但Redis没有在事务上增加任何维持原子性的机制, 所以Redis事务的执行并不是原子性的, 如果一个事务队列中的所有命令都被成功的执行, 那么称这个事务执行成功
- 一致性(Consistency)
- 入队错误
- 在命令入队的过程中, 如果客户端向服务器发送了错误的命令, 比如命令命令参数数量不对,等等, 那么服务器向客户端返回一个出错信息, 并且将客户端的事务状态设为 REDIS_DIRTY_EXEC
- 执行错误
- 如果命令在事务执行的过程中发生错误, 比如说, 对一个不同类型的key执行了错误的错误, 那么Redis只会将错误包含在事务的结果中, 这不会引起事务中断或这个失败, 不会影响已执行事务命令的结果, 也不会影响后续要执行的事务命令, 所以他对事务的一致性也没有影响
- 隔离性(Isolation)
- WATCH 命令用于在事务开始之前见识任意数量的键, 当调用EXEC命令执行事务时, 如果任意一个被监控的键被其他客户端修改了, 那么整个事务不再执行, 直接返回失败
- 持久性(Durability)
- 因为事务不过是用队列包裹起了一组Redis命令, 并没有提供任何额外的持久性功能, 所以事务的持久性由redis所使用的持久化模式决定
- redis持久化模式包括:
- rdb: 数据内存备份
- aof(append onlyif增量备份), 每个一秒做一个备份
springboot整合redis
- 引入依赖
org.springframework.boot
spring-boot-starter-data-redis
2.1.7.RELEASE
- 引入bean
- redisTemplate的使用, 类似于: mongoTemplate, jdbcTemplate数据库链接工具, RedisTemplate是从redisConnectionFactory中获取的
- 引入jar包 spring-boot-starter-data-redis
- 实例代码
- RedisConfig.java
package cn.jishupeng.redis.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
- textController.java
package cn.jishupeng.redis.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@RestController
public class test {
@Resource
private RedisTemplate redisTemplate;
@GetMapping("/setAndget")
public String index(String name) {
redisTemplate.opsForValue().set("name", name);
return (String)redisTemplate.opsForValue().get("name");
}
}
redisTemplate api
- opsForValue -> String
- opsForSet -> Set
- opsForHash -> Hash
- opsForZset -> SortSet
- opsForList -> List
springboot快速整合mybatis(极简 mysql版本8.X)
- 加入依赖
mysql
mysql-connector-java
8.0.11
com.alibaba
druid
1.1.10
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.0.1
org.springframework
spring-jdbc
5.1.5.RELEASE
- 配置application.properties文件
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/redis?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false&allowP
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
- 启动类加上@MapperScan(basePackage = )注解
package cn.jishupeng.redis;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan(basePackages = "cn.jishupeng.redis.mapper")
@SpringBootApplication
public class RedisApplication {
public static void main(String[] args) {
SpringApplication.run(RedisApplication.class, args);
}
}
- 创建UserMapper包(该文件下都是接口文件, 每个文件都要加上@Mapper注解, 否则扫描不到)
package cn.jishupeng.redis.mapper;
import cn.jishupeng.redis.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("select * from user")
User find();
}
- domain/User 实体类文件
package cn.jishupeng.redis.domain;
import org.springframework.stereotype.Repository;
public class User {
private int id;
private String username;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
'}';
}
}
- controller/TestController(常规使用, 直接使用@Autowired注入UserMapper文件, 就可以快乐玩耍啦...)
package cn.jishupeng.redis.controller;
import cn.jishupeng.redis.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@RestController
public class test {
@Autowired
private UserMapper userMapper;
@Resource
private RedisTemplate redisTemplate;
@GetMapping("/setAndget")
public String index(String name) {
redisTemplate.opsForValue().set("name", name);
return (String)redisTemplate.opsForValue().get("name");
}
@GetMapping("/getUser")
public String getUser() {
return (String)userMapper.find().getUsername();
}
}
redis作为mybatis缓存整合
- 用户第一次访问的时候获取数据库的值, 再次访问时直接从缓存中获取数据
- 设置缓存过期时间
- 项目8080端口是对外端口(向外部暴露的端口), 区别内部进程号, 查内部端口用ps -ef|grep, 查外部端口用 lsof -i:8080
- 缓存级别
- 一级缓存: sqlSession, sql建立链接到关闭链接的数据缓存
- 二级缓存: 全局
- springboot cache的使用: 可以结合redis, ehcache等缓存
- @Cacheable: (查询时使用)来划分可缓存的方法, 即, 结果存储在缓存中的方法, 以便在后续调用(具有相同的参数)时, 返回缓存中的值而不必实际执行该方法
- @CachePut: (更新时使用)当需要更新缓存而不干扰方法执行时, 可以使用@CachePut注释, 也就是说, 始终执行该方法并将其结果放入缓存中(根据@CachePut选项)
- @CacheEvict:(删除时使用) 对于从缓存中删除旧或未使用的数据非常有用, 指示缓存范围内的驱逐是否需要执行而不仅仅是一个条目驱逐
- springboot整合cache的步骤
- 引入依赖 spring-boot-starter-cache
- 开启缓存注解: @EnableCaching
- 在方法上面加入SpEL
redis分布式缓存
- 引入依赖:
org.springframework.session
spring-session-data-redis
2.1.4.RELEASE
- 加入注解 @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 50)
- controller方法
package cn.jishupeng.redis.controller;
import org.springframework.http.HttpRequest;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@RequestMapping("/session")
@RestController
public class RedisHttpSession {
@RequestMapping("/set")
@ResponseBody
public Map set(HttpServletRequest request) {
Map ret = new HashMap<>();
request.getSession().setAttribute("request Uri", request.getRequestURI());
ret.put("request Uri", request.getRequestURI());
return ret;
}
@RequestMapping("/get")
@ResponseBody
public Map get(HttpServletRequest request) {
Map ret = new HashMap<>();
Object id = request.getSession().getId();
ret.put("request Uri", id);
return ret;
}
}
- redis客户端中
- keys * 查找所有的键
- ttl 查看超时时间
redis项目实战值排行榜实现(上)
- 排行榜
- 排行榜功能是一个很普遍的需求, 使用redis中有序集合的特性来实现排行榜是又好又快的实现
- 一般排行榜都是有时效性, 比如""用户积分榜", 游戏中活跃度排行榜, 游戏装备排行榜等等
- 面临问题: 数据库设计复杂, 并发较高, 数据要求实时性高
- redis实现排行榜api讲解
浅谈mysql数据库设计
- 表设计过程中应该注意的点即数据类型
- 更小的通常更好, 控制字节长度
- 使用合适的数据类型
- 如tinyint只占8个位, char与varchar的对比
- 尽量避免NULL NOT NULL DEFAULT ''
- NULL的列会让索引统计和值比较都更复杂. 可为NULL的列会占据更多的磁盘空间, 在Mysql中也需要更多复杂的处理程序
- 索引设计过程中更应该注意的点
- 选择唯一性索引
- 唯一性索引的值是唯一的, 可以更快的通过该索引来确定某条记录
- 为经常需要排序, 分组和联合操作的字段建立索引
- 经常需要order by, group by, distinct和union等操作的字段, 排序操作会浪费更多的时间
- 长作为查询条件的字段建立索引
- 如果某个字段常用来做查询条件, 那么该字段的查询速度会影响整个表的查询速度
- 数据少的地方不必建立索引
- sql优化, explain查看执行计划
- 能够用between的就不要用in
- 能够用distinct的就不要用group by
- 避免数据强转
- 学会采用wxplain查看执行计划