刘沙河 刘沙河
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希

花开半夏,半夏花开
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希
  • Mysql

  • Redis

    • redis基础和数据类型
    • redis缓存穿透、缓存雪崩、缓存击穿
    • redis高阶使用
    • 布隆过滤器与缓存穿透
    • redis持久化存储
    • redis线程模型
    • redis过期策略
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • redis高并发和高可用
    • 如何保证缓存和数据库双写一致
    • redis为什么那么快
      • 1. redis为什么那么快?
      • 2. Redis有几种数据结构,底层分别是怎么存储的?
      • 3. go 如何调用lua脚本执行redis命令
    • redis分布式锁
    • redis事务和watch
    • redis 底层数据结构
  • elasticsearch

  • etcd

  • Database
  • Redis
bigox
2022-07-01
目录

redis为什么那么快

# 1. redis为什么那么快?

1.1 基于内存存储实现

  • 内存读写是比在磁盘快很多的,Redis基于内存存储实现的数据库,相对于数据存在磁盘的MySQL数据库,省去磁盘1/0的消耗。

1.2 高效的数据结构

  • redis 数据结构和内部编码

    img

  1. SDS简单动态字符串

    image-20220626154909684

    字符串长度处理:Redis获取字符串长度,时间复杂度为0(1),而C语言中,需要从头开始遍历,复杂度为0(n);

  2. 字典

    redis 是一种key-value内存数据库, 搜友的键值都是用字典来存储的.

1.3 合理的数据编码

Redis 支持多种数据类型,每种基本类型,可能对多种数据结构。什么时候,使用什么样数据结构,使用什么样 编码,是redis设计者总结优化的结果。

  • string:如果存储数字的话,是用int类型的编码;如果存储非数宇,小于等于39字节的字符串,是embstr;大于39个字节,则是raw编码。

  • list:如果列表的元素个数小于512个,列表每个元素的值都小于64字节(默认),使用ziplist编码,否则使用linkedlist编码

  • Hash:哈希类型元素个数小于512个,所有值小于64字节的话,使用ziplist编码,否则使用hashtable编码。

  • set:如果集合中的元素都是整数且元素个数小于512个,使用intset编码,否则使用hashtable编码、

  • zset:当有序集合的元素个数小于128个,每个元素的值小于64字节时,使用ziplist编码,否则使用skiplist(跳跃表)编码

1.4 合理的io线程模型(io多路复用)

  • 多路1/0复用技术可以让单个线程高效的处理多个连接请求,而Redis使用用epol作为1/0多路复用技术的实现。并且,Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络1/0上浪费过多的时间

# 2. Redis有几种数据结构,底层分别是怎么存储的?

  • 基础数据类型
    1. string
    2. list
    3. set
    4. zset
    5. hash

2.1 String(字符串)

  • 简介:String是Redis最基础的数据结构类型,它是二进制安全的,可以存储图片或者序列化的对象,值最大存储为512M
  • 简单使用举例:set key value、get key等
  • 应用场景:共享session、分布式锁,计数器、限流。
  • 内部编码有3种,int (8字节长整型)/embstr(小于等于39字节字符串)/raw(大于39个字节字符串)

2.2 Hash(哈希)

  • 简介:在Redis中,哈希类型是指v(值)本身又是一个键值对(k-v)结构
  • 简单使用举例:hset key field value、hget key field
  • 内部编码:ziplist(压缩列表)、hashtable(哈希表)
  • 应用场景:缓存用户信息等。

2.3 List(列表)

  • 简介:列表 (list)类型是用来存储多个有序的字符串,一个列表最多可以存储2^32-1个元素。
  • 简单实用举例:lpush key value Ivalue ..J、lrange key start end
  • 内部编码:ziplist(压缩列表)、linkedlist (链表)
  • 应用场景:消息队列,文章列表

2.4 Set(集合)

  • 简介:集合(set)类型也是用来保存多个的字符串元素,,但是不允许重复元素
  • 简单使用举例:sadd key element [element ..]smembers key
  • 内部编码:intset (整数集合)、hashtable(哈希)
  • 使用场景:用户标签,生成随机数抽奖、社交需求。

2.5 Zset(有序集合)

  • 简介:已排序的字符串集合,同时元素不能重复
  • 简单格式举例:zadd key score member [score member…], zrank key member
  • 底层内部编码:ziplist(压缩列表)、skiplist(跳跃表)
  • 应用场景:排行榜,社交需求(如用户点赞)

# 3. go 如何调用lua脚本执行redis命令

package main

import (
	"fmt"
	"github.com/gomodule/redigo/redis"
	"io/ioutil"
	"time"
)

func main() {
	// 测试redis连接是否正常
	dial, err := redis.Dial("tcp", "10.157.89.90:8379")
	if err != nil {
		panic(err)
		return
	}
	dial.Close()

	pool := &redis.Pool{
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", "10.157.89.90:8379")
			if err != nil {
				fmt.Println(err)
				return nil, err
			}
			return c, nil
		},
		DialContext:     nil,
		TestOnBorrow:    nil,
		MaxIdle:         10,
		MaxActive:       10,
		IdleTimeout:     10 * time.Second,
		Wait:            false,
		MaxConnLifetime: 0,
	}

	conn := pool.Get()
	defer conn.Close()

	file, err := ioutil.ReadFile("script/tokenbucket.lua")
	if err != nil {
		panic(err)
		return
	}

	lua := redis.NewScript(1, string(file))
	// 初始化提前加载脚本
	err = lua.Load(conn)
	if err != nil {
		panic(err)
	}

	// 调用脚本,如果之前没有加载过脚本,Do 方法也会加载脚本
	ret, err := lua.Do(conn, "lua", 0, 0, 0, 0, 0)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("%#v", string(ret.([]byte)))

	// Send 方法不会等待返回结果,只是将命令写到缓存,需要调用redis.Conn的Flush 方法
	err = lua.Send(conn, "lua2", 0, 0, 0, 0, 0)
	if err != nil {
		fmt.Println(err)
		return
	}

	conn.Flush()
	// conn.Close()内部会执行一次 Do 方法,可相当于Flush
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#数据库#
上次更新: 2023/04/16, 18:35:33
如何保证缓存和数据库双写一致
redis分布式锁

← 如何保证缓存和数据库双写一致 redis分布式锁→

最近更新
01
go与http代理
05-24
02
自制申威架构k8s-reloader
12-06
03
Docker Buildx 教程
12-01
更多文章>
Theme by Vdoing | Copyright © 2020-2024 小刘扎扎 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式