2023-03-27
redis
00
请注意,本文编写于 667 天前,最后修改于 667 天前,其中某些信息可能已经过时。

目录

Lua

Lua

是一种轻量的脚本语言,设计目的是为了嵌入应用程序中,为应用程序提供灵活的扩展和定制功能

特性:

  • 轻量级 用标准c语言编写并以源代码形式开放
  • 可扩展 提供了非常易于使用的扩展接口和机制
  • 支持面向过程编程和函数式编程
  • 自动内存管理,只提供了一种通用类型的表,可以用它实现数组、哈希表、集合、对象

这里主要是用redis的时候了解了一下lua脚本,所以这里主要记录redis中的使用

  • 减少网络开销,将多个请求通过脚本的形式一次性发送,减少时延
  • 原子操作,会将整个脚本作为一个整体执行,中间不会被其他请求插入
  • 复用:客户端发送的脚本会永久存在redis中,其余客户端就可以复用这一脚本

lua可以用redis.call()调用redis命令

lua
return redis.call('GET',KEYS[1])

redis中的一些lua命令

  • EVAL
    • EVAL script numkeys key[key ...] arg[arg ...]
    • script是脚本程序
    • numkeys指定后续的key个数
    • key表示在脚本中所用的Redis键,在Lua脚本中通过KEYS[1],KEYS[2]获取
    • arg附加参数,ARGV[1]获取
lua
-- 相当于SETEX key1 60 10
EVAL "redis.call('SET',KEYS[1],ARGV[1]);redis.call("EXIPRE',KEYS[1],ARGV[2]);return 1" 1 key1 10 60
  • SCRIPT LOAD

    • SCRIPT LOAD script
    • SCRIPT LOAD 将脚本script添加到Redis服务器的脚本缓存中,并不立即执行,而是对输入的脚本进行求值,并返回给定脚本的SHA1校验和,如果给定的脚本已经在缓存里面,那么不执行任何操作
  • EVALSHA

    • EVALSHA sha1 numkeys key[key...] arg[arg...]
    • 在脚本加入到缓存之后,在任何客户端通过EVALSHA命令,可以使用脚本的SHA1校验和来调用这个脚本,脚本可以在缓存中一直保存直到缓存被清除(SCRIPT FLUSH)
  • SCRIPT EXISTS

    • SCRIPT EXISTS sha1[sha1...]
    • 给定一个或多个脚本的SHA1校验和,返回一个包含0和1的列表,表示校验和所指定的脚本是否已经被保存到缓存当中
  • SCRIPT FLUSH

    • SCRIPT FLUSH
    • 清除redis服务端所有lua脚本缓存
  • SCRIPT KILL

    • SCRIPT KILL
    • 杀死正在运行的lua脚本,当且仅当这个脚本没有进行任何写操作时,这个命令才生效,主要用于终止运行时间过长的脚本,比如一个因为Bug而无限loop的脚本
    • 加入当前正在运行的脚本已经执行过写操作,那么无法杀死,否则违反了lua脚本的原子性,在这种情况下只能使用SHUTDOWN NOSAVE命令,停止整个redis进程来停止脚本的运行,防止不完整的信息写入数据库

消费者案例,主要是进行原子操作以及防抖重复购买,以及避免超卖

golang
package main

import (
	"fmt"
	"github.com/gomodule/redigo/redis"
	"math/rand"
	_redis "redis-example/redis"
	"time"
)

const Script = `
if redis.call("EXISTS",KEYS[1]) ==1 then
    return 2
else
    if redis.call("EXISTS",KEYS[2]) == 1 and tonumber(redis.call("GET",KEYS[2]))>0 then
        redis.call("DECR",KEYS[2])
        redis.call("HINCRBY",KEYS[1].."buy",KEYS[2],1)
        redis.call("SET",KEYS[1],1)
        redis.call("EXPIRE",KEYS[1],1)
        return 1
    else
        return 0  
    end
end
`

func producer(c redis.Conn, product string) {
	defer c.Close()
	for {
		// 自增
		c.Do("INCR", product)
		fmt.Println(product + "+1")
		time.Sleep(time.Second)
	}
}

func consumer(c redis.Conn, user string, product string) {
	defer c.Close()
	lua := redis.NewScript(2, Script)
	for {
		res, err := lua.Do(c, user, product)
		if err != nil {
			fmt.Println(err, 111)
			return
		}
		if r, _ := res.(int64); r == 1 {
			fmt.Println(user + " buy " + product)
		} else if r == 0 {
			fmt.Println("Unlucky " + user + "," + " the " + product + " is sold out!")
		} else {
			fmt.Println(user + " you hava buy in this second")
		}
		time.Sleep(time.Millisecond * time.Duration(rand.Intn(10)*100))
	}
}

func main() {
	c := _redis.GetRedisConn()
	c.Do("FLUSHALL")
	c.Close()

	go producer(_redis.GetRedisConn(), "apple")
	go producer(_redis.GetRedisConn(), "pineapple")
	go consumer(_redis.GetRedisConn(), "1", "apple")
	go consumer(_redis.GetRedisConn(), "2", "apple")
	go consumer(_redis.GetRedisConn(), "2", "pineapple")
	time.Sleep(time.Second * 10)
	//go consumer(_redis.GetRedisConn(), "用户1")
}

本文作者:Malyue

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!