欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用Redis實(shí)現(xiàn)分布式鎖的方法

 更新時(shí)間:2022年06月16日 10:34:18   作者:ZhanLi  
為了保證我們線上服務(wù)的并發(fā)性和安全性,目前我們的服務(wù)一般拋棄了單體應(yīng)用,采用的都是擴(kuò)展性很強(qiáng)的分布式架構(gòu),這篇文章主要介紹了使用Redis實(shí)現(xiàn)分布式鎖的方法,需要的朋友可以參考下

Redis 中的分布式鎖如何使用

分布式鎖的使用場(chǎng)景

為了保證我們線上服務(wù)的并發(fā)性和安全性,目前我們的服務(wù)一般拋棄了單體應(yīng)用,采用的都是擴(kuò)展性很強(qiáng)的分布式架構(gòu)。

對(duì)于可變共享資源的訪問,同一時(shí)刻,只能由一個(gè)線程或者進(jìn)程去訪問操作。這時(shí)候我們就需要做個(gè)標(biāo)識(shí),如果當(dāng)前有線程或者進(jìn)程在操作共享變量,我們就做個(gè)標(biāo)記,標(biāo)識(shí)當(dāng)前資源正在被操作中, 其它的線程或者進(jìn)程,就不能進(jìn)行操作了。當(dāng)前操作完成之后,刪除標(biāo)記,這樣其他的線程或者進(jìn)程,就能來申請(qǐng)共享變量的操作。通過上面的標(biāo)記來保證同一時(shí)刻共享變量只能由一個(gè)線程或者進(jìn)行持有。

對(duì)于單體應(yīng)用:多個(gè)線程之間訪問可變共享變量,比較容易處理,可簡(jiǎn)單使用內(nèi)存來存儲(chǔ)標(biāo)示即可;

分布式應(yīng)用:這種場(chǎng)景下比較麻煩,因?yàn)槎鄠€(gè)應(yīng)用,部署的地址可能在不同的機(jī)房,一個(gè)在北京一個(gè)在上海。不能簡(jiǎn)單的存儲(chǔ)標(biāo)示在內(nèi)存中了,這時(shí)候需要使用公共內(nèi)存來記錄該標(biāo)示,栗如 Redis,MySQL 。。。

使用 Redis 來實(shí)現(xiàn)分布式鎖

這里來聊聊如何使用 Redis 實(shí)現(xiàn)分布式鎖

Redis 中分布式鎖一般會(huì)用 set key value px milliseconds nx 或者 SETNX+Lua來實(shí)現(xiàn)。

因?yàn)?SETNX 命令,需要配合 EXPIRE 設(shè)置過期時(shí)間,Redis 中單命令的執(zhí)行是原子性的,組合命令就需要使用 Lua 才能保證原子性了。

看下如何實(shí)現(xiàn)

使用 set key value px milliseconds nx 實(shí)現(xiàn)

因?yàn)檫@個(gè)命令同時(shí)能夠設(shè)置鍵值和過期時(shí)間,同時(shí)Redis中的單命令都是原子性的,所以加鎖的時(shí)候使用這個(gè)命令即可

func (r *Redis) TryLock(ctx context.Context, key, value string, expire time.Duration) (isGetLock bool, err error) {
	// 使用 set nx
	res, err := r.Do(ctx, "set", key, value, "px", expire.Milliseconds(), "nx").Result()
	if err != nil {
		return false, err
	}
	if res == "OK" {
		return true, nil
	}
	return false, nil
}

SETNX+Lua 實(shí)現(xiàn)

如果使用 SETNX 命令,這個(gè)命令不能設(shè)置過期時(shí)間,需要配合 EXPIRE 命令來使用。

因?yàn)槭怯玫搅藘蓚€(gè)命令,這時(shí)候兩個(gè)命令的組合使用是不能保障原子性的,在一些并發(fā)比較大的時(shí)候,需要配合使用 Lua 腳本來保證命令的原子性。

func tryLockScript() string {
	script := `
		local key = KEYS[1]

		local value = ARGV[1] 
		local expireTime = ARGV[2] 
		local isSuccess = redis.call('SETNX', key, value)

		if isSuccess == 1 then
			redis.call('EXPIRE', key, expireTime)
			return "OK"
		end

		return "unLock"    `
	return script
}

func (r *Redis) TryLock(ctx context.Context, key, value string, expire time.Duration) (isGetLock bool, err error) {
	// 使用 Lua + SETNX
	res, err := r.Eval(ctx, tryLockScript(), []string{key}, value, expire.Seconds()).Result()
	if err != nil {
		return false, err
	}
	if res == "OK" {
		return true, nil
	}
	return false, nil
}

除了上面加鎖兩個(gè)命令的區(qū)別之外,在解鎖的時(shí)候需要注意下不能誤刪除別的線程持有的鎖

為什么會(huì)出現(xiàn)這種情況呢,這里來分析下

舉個(gè)栗子

1、線程1獲取了鎖,鎖的過期時(shí)間為1s;

2、線程1完成了業(yè)務(wù)操作,用時(shí)1.5s ,這時(shí)候線程1的鎖已經(jīng)被過期時(shí)間自動(dòng)釋放了,這把鎖已經(jīng)被別的線程獲取了;

3、但是線程1不知道,接著去釋放鎖,這時(shí)候就會(huì)將別的線程的鎖,錯(cuò)誤的釋放掉。

面對(duì)這種情況,其實(shí)也很好處理

1、設(shè)置 value 具有唯一性;

2、每次刪除鎖的時(shí)候,先去判斷下 value 的值是否能對(duì)的上,不相同就表示,鎖已經(jīng)被別的線程獲取了;

看下代碼實(shí)現(xiàn)

var UnLockErr = errors.New("未解鎖成功")
func unLockScript() string {
	script := `
		local value = ARGV[1] 
		local key = KEYS[1]
		local keyValue = redis.call('GET', key)
		if tostring(keyValue) == tostring(value) then
			return redis.call('DEL', key)
		else
			return 0
		end
    `
	return script
}
func (r *Redis) Unlock(ctx context.Context, key, value string) (bool, error) {
	res, err := r.Eval(ctx, unLockScript(), []string{key}, value).Result()
	if err != nil {
		return false, err
	}
	return res.(int64) != 0, nil
}

代碼可參考lock

上面的這類鎖的最大缺點(diǎn)就是只作用在一個(gè)節(jié)點(diǎn)上,即使 Redis 通過 sentinel 保證高可用,如果這個(gè) master 節(jié)點(diǎn)由于某些原因放生了主從切換,那么就會(huì)出現(xiàn)鎖丟失的情況:

1、在 Redis 的 master 節(jié)點(diǎn)上拿到了鎖;

2、但是這個(gè)加鎖的 key 還沒有同步到 slave 節(jié)點(diǎn);

3、master 故障,發(fā)生了故障轉(zhuǎn)移,slave 節(jié)點(diǎn)升級(jí)為 master 節(jié)點(diǎn);

4、導(dǎo)致鎖丟失。

針對(duì)這種情況如何處理呢,下面來聊聊 Redlock 算法

使用 Redlock 實(shí)現(xiàn)分布式鎖

在 Redis 的分布式環(huán)境中,我們假設(shè)有 N 個(gè) Redis master。這些節(jié)點(diǎn)完全互相獨(dú)立,不存在主從復(fù)制或者其他集群協(xié)調(diào)機(jī)制。我們確保將在 N 個(gè)實(shí)例上使用與在 Redis 單實(shí)例下相同方法獲取和釋放鎖?,F(xiàn)在我們假設(shè)有 5 個(gè) Redis master 節(jié)點(diǎn),同時(shí)我們需要在5臺(tái)服務(wù)器上面運(yùn)行這些 Redis 實(shí)例,這樣保證他們不會(huì)同時(shí)都宕掉。

為了取到鎖,客戶端營(yíng)該執(zhí)行以下操作:

1、獲取當(dāng)前Unix時(shí)間,以毫秒為單位。

2、依次嘗試從5個(gè)實(shí)例,使用相同的key和具有唯一性的 value(例如UUID)獲取鎖。當(dāng)向 Redis 請(qǐng)求獲取鎖時(shí),客戶端應(yīng)該設(shè)置一個(gè)網(wǎng)絡(luò)連接和響應(yīng)超時(shí)時(shí)間,這個(gè)超時(shí)時(shí)間應(yīng)該小于鎖的失效時(shí)間。例如你的鎖自動(dòng)失效時(shí)間為10秒,則超時(shí)時(shí)間應(yīng)該在 5-50 毫秒之間。這樣可以避免服務(wù)器端 Redis 已經(jīng)掛掉的情況下,客戶端還在死死地等待響應(yīng)結(jié)果。如果服務(wù)器端沒有在規(guī)定時(shí)間內(nèi)響應(yīng),客戶端應(yīng)該盡快嘗試去另外一個(gè) Redis 實(shí)例請(qǐng)求獲取鎖;

3、客戶端使用當(dāng)前時(shí)間減去開始獲取鎖時(shí)間(步驟1記錄的時(shí)間)就得到獲取鎖使用的時(shí)間。當(dāng)且僅當(dāng)從大多數(shù)(N/2+1,這里是3個(gè)節(jié)點(diǎn))的 Redis 節(jié)點(diǎn)都取到鎖,并且使用的時(shí)間小于鎖失效時(shí)間時(shí),鎖才算獲取成功;

4、如果取到了鎖,key 的真正有效時(shí)間等于有效時(shí)間減去獲取鎖所使用的時(shí)間(步驟3計(jì)算的結(jié)果);

5、如果因?yàn)槟承┰?,獲取鎖失?。]有在至少N/2+1個(gè) Redis 實(shí)例取到鎖或者取鎖時(shí)間已經(jīng)超過了有效時(shí)間),客戶端應(yīng)該在所有的Redis實(shí)例上進(jìn)行解鎖(即便某些 Redis 實(shí)例根本就沒有加鎖成功,防止某些節(jié)點(diǎn)獲取到鎖但是客戶端沒有得到響應(yīng)而導(dǎo)致接下來的一段時(shí)間不能被重新獲取鎖)。

根據(jù)官方的推薦,go 版本中 Redsync 實(shí)現(xiàn)了這一算法,這里看下具體的實(shí)現(xiàn)過程

redsync項(xiàng)目地址

// LockContext locks m. In case it returns an error on failure, you may retry to acquire the lock by calling this method again.
func (m *Mutex) LockContext(ctx context.Context) error {
	if ctx == nil {
		ctx = context.Background()
	}
	value, err := m.genValueFunc()
	if err != nil {
		return err
	}
	
	for i := 0; i < m.tries; i++ {
		if i != 0 {
			select {
			case <-ctx.Done():
				// Exit early if the context is done.
				return ErrFailed
			case <-time.After(m.delayFunc(i)):
				// Fall-through when the delay timer completes.
			}
		}
		start := time.Now()
		// 嘗試在所有的節(jié)點(diǎn)中加鎖
		n, err := func() (int, error) {
			ctx, cancel := context.WithTimeout(ctx, time.Duration(int64(float64(m.expiry)*m.timeoutFactor)))
			defer cancel()
			return m.actOnPoolsAsync(func(pool redis.Pool) (bool, error) {
				// acquire 加鎖函數(shù)
				return m.acquire(ctx, pool, value)
			})
		}()
		if n == 0 && err != nil {
			return err
		}
		// 如果加鎖節(jié)點(diǎn)書沒有達(dá)到的設(shè)定的數(shù)目
		// 或者鍵值的過期時(shí)間已經(jīng)到了
		// 在所有的節(jié)點(diǎn)中解鎖
		now := time.Now()
		until := now.Add(m.expiry - now.Sub(start) - time.Duration(int64(float64(m.expiry)*m.driftFactor)))
		if n >= m.quorum && now.Before(until) {
			m.value = value
			m.until = until
			return nil
		}
		_, err = func() (int, error) {
			ctx, cancel := context.WithTimeout(ctx, time.Duration(int64(float64(m.expiry)*m.timeoutFactor)))
			defer cancel()
			return m.actOnPoolsAsync(func(pool redis.Pool) (bool, error) {
				// 解鎖函數(shù)
				return m.release(ctx, pool, value)
			})
		}()
		if i == m.tries-1 && err != nil {
			return err
		}
	}
	return ErrFailed
}
// 遍歷所有的節(jié)點(diǎn),并且在每個(gè)節(jié)點(diǎn)中執(zhí)行傳入的函數(shù)
func (m *Mutex) actOnPoolsAsync(actFn func(redis.Pool) (bool, error)) (int, error) {
	type result struct {
		Status bool
		Err    error
	}
	ch := make(chan result)
	// 執(zhí)行傳入的函數(shù)
	for _, pool := range m.pools {
		go func(pool redis.Pool) {
			r := result{}
			r.Status, r.Err = actFn(pool)
			ch <- r
		}(pool)
	}
	n := 0
	var err error
	// 計(jì)算執(zhí)行成功的節(jié)點(diǎn)數(shù)目
	for range m.pools {
		r := <-ch
		if r.Status {
			n++
		} else if r.Err != nil {
			err = multierror.Append(err, r.Err)
		}
	}
	return n, err
}
// 手動(dòng)解鎖的lua腳本
var deleteScript = redis.NewScript(1, `
	if redis.call("GET", KEYS[1]) == ARGV[1] then
		return redis.call("DEL", KEYS[1])
	else
		return 0
	end
`)
// 手動(dòng)解鎖
func (m *Mutex) release(ctx context.Context, pool redis.Pool, value string) (bool, error) {
	conn, err := pool.Get(ctx)
	if err != nil {
		return false, err
	}
	defer conn.Close()
	status, err := conn.Eval(deleteScript, m.name, value)
	if err != nil {
		return false, err
	}
	return status != int64(0), nil
}

分析下思路

1、遍歷所有的節(jié)點(diǎn),然后嘗試在所有的節(jié)點(diǎn)中執(zhí)行加鎖的操作;

2、收集加鎖成功的節(jié)點(diǎn)數(shù),如果沒有達(dá)到指定的數(shù)目,釋放剛剛添加的鎖;

關(guān)于 Redlock 的缺點(diǎn)可參見

How to do distributed locking

鎖的續(xù)租

Redis 中分布式鎖還有一個(gè)問題就是鎖的續(xù)租問題,當(dāng)鎖的過期時(shí)間到了,但是業(yè)務(wù)的執(zhí)行時(shí)間還沒有完成,這時(shí)候就需要對(duì)鎖進(jìn)行續(xù)租了

續(xù)租的流程

1、當(dāng)客戶端加鎖成功后,可以啟動(dòng)一個(gè)定時(shí)的任務(wù),每隔一段時(shí)間,檢查業(yè)務(wù)是否完成,未完成,增加 key 的過期時(shí)間;

2、這里判斷業(yè)務(wù)是否完成的依據(jù)是:

  • 1、這個(gè) key 是否存在,如果 key 不存在了,就表示業(yè)務(wù)已經(jīng)執(zhí)行完成了,也就不需要進(jìn)行續(xù)租操作了;
  • 2、同時(shí)需要校驗(yàn)下 value 值,如果 value 對(duì)應(yīng)的值和之前寫入的值不同了,說明當(dāng)前鎖已經(jīng)被別的線程獲取了;

看下 redsync 中續(xù)租的實(shí)現(xiàn)

// Extend resets the mutex's expiry and returns the status of expiry extension.
func (m *Mutex) Extend() (bool, error) {
	return m.ExtendContext(nil)
}

// ExtendContext resets the mutex's expiry and returns the status of expiry extension.
func (m *Mutex) ExtendContext(ctx context.Context) (bool, error) {
	start := time.Now() 
	// 嘗試在所有的節(jié)點(diǎn)中加鎖
	n, err := m.actOnPoolsAsync(func(pool redis.Pool) (bool, error) {
		return m.touch(ctx, pool, m.value, int(m.expiry/time.Millisecond))
	})
	if n < m.quorum {
		return false, err
	}
	// 判斷下鎖的過期時(shí)間
	now := time.Now()
	until := now.Add(m.expiry - now.Sub(start) - time.Duration(int64(float64(m.expiry)*m.driftFactor)))
	if now.Before(until) {
		m.until = until
		return true, nil
	}
	return false, ErrExtendFailed
}

var touchScript = redis.NewScript(1, `
	// 需要先比較下當(dāng)前的value值
	if redis.call("GET", KEYS[1]) == ARGV[1] then
		return redis.call("PEXPIRE", KEYS[1], ARGV[2])
	else
		return 0
	end
`)

func (m *Mutex) touch(ctx context.Context, pool redis.Pool, value string, expiry int) (bool, error) {
	conn, err := pool.Get(ctx)
	if err != nil {
		return false, err
	}
	defer conn.Close()
	status, err := conn.Eval(touchScript, m.name, value, expiry)
	if err != nil {
		return false, err
	}
	return status != int64(0), nil
}

1、鎖的續(xù)租需要客戶端去監(jiān)聽和操作,啟動(dòng)一個(gè)定時(shí)器,固定時(shí)間來調(diào)用續(xù)租函數(shù)給鎖續(xù)租;

2、每次續(xù)租操作的時(shí)候需要匹配下當(dāng)前的 value 值,因?yàn)殒i可能已經(jīng)被當(dāng)前的線程釋放了,當(dāng)前的持有者可能是別的線程;

看看 SETEX 的源碼

SETEX 能保證只有在 key 不存在時(shí)設(shè)置 key 的值,那么這里來看看,源碼中是如何實(shí)現(xiàn)的呢

// https://github.com/redis/redis/blob/7.0/src/t_string.c#L78
// setGenericCommand()函數(shù)是以下命令: SET, SETEX, PSETEX, SETNX.的最底層實(shí)現(xiàn)
void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
    ...

    found = (lookupKeyWrite(c->db,key) != NULL);
    // 這里是 SETEX 實(shí)現(xiàn)的重點(diǎn)
	// 如果nx,并且在數(shù)據(jù)庫(kù)中找到了這個(gè)值就返回
	// 如果是 xx,并且在數(shù)據(jù)庫(kù)中沒有找到鍵值就會(huì)返回
	
	// 因?yàn)?Redis 中的命令執(zhí)行都是單線程操作的
	// 所以命令中判斷如果存在就返回,能夠保證正確性,不會(huì)出現(xiàn)并發(fā)訪問的問題
    if ((flags & OBJ_SET_NX && found) ||
        (flags & OBJ_SET_XX && !found))
    {
        if (!(flags & OBJ_SET_GET)) {
            addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
        }
        return;
    }

    ...
}

1、命令的實(shí)現(xiàn)里面加入了鍵值是否存在的判斷,來保證 NX 只有在 key 不存在時(shí)設(shè)置 key 的值;

2、因?yàn)?Redis 中總是一個(gè)線程處理命令的執(zhí)行,單命令是能夠保證原子性,不會(huì)出現(xiàn)并發(fā)的問題。

為什么 Redis 可以用來做分布式鎖

分布式鎖需要滿足的特性

  • 互斥性:在任意時(shí)刻,對(duì)于同一個(gè)鎖,只有一個(gè)客戶端能持有,從而保證一個(gè)共享資源同一時(shí)間只能被一個(gè)客戶端操作;
  • 安全性:即不會(huì)形成死鎖,當(dāng)一個(gè)客戶端在持有鎖的期間崩潰而沒有主動(dòng)解鎖的情況下,其持有的鎖也能夠被正確釋放,并保證后續(xù)其它客戶端能加鎖;
  • 可用性:當(dāng)提供鎖服務(wù)的節(jié)點(diǎn)發(fā)生宕機(jī)等不可恢復(fù)性故障時(shí),“熱備” 節(jié)點(diǎn)能夠接替故障的節(jié)點(diǎn)繼續(xù)提供服務(wù),并保證自身持有的數(shù)據(jù)與故障節(jié)點(diǎn)一致。
  • 對(duì)稱性:對(duì)于任意一個(gè)鎖,其加鎖和解鎖必須是同一個(gè)客戶端,即客戶端 A 不能把客戶端 B 加的鎖給解了。

那么 Redis 對(duì)上面的特性是如何支持的呢?

1、Redis 中命令的執(zhí)行都是單線程的,雖然在 Redis6.0 的版本中,引入了多線程來處理 IO 任務(wù),但是命令的執(zhí)行依舊是單線程處理的;

2、單線程的特點(diǎn),能夠保證命令的執(zhí)行的是不存在并發(fā)的問題,同時(shí)命令執(zhí)行的原子性也能得到保證;

3、Redis 中提供了針對(duì) SETNX 這樣的命令,能夠保證同一時(shí)刻是只會(huì)有一個(gè)請(qǐng)求執(zhí)行成功,提供互斥性的保障;

4、Redis 中也提供了 EXPIRE 超時(shí)釋放的命令,可以實(shí)現(xiàn)鎖的超時(shí)釋放,避免死鎖的出現(xiàn);

5、高可用,針對(duì)如果發(fā)生主從切換,數(shù)據(jù)丟失的情況,Redis 引入了 RedLock 算法,保證了 Redis 中主要大部分節(jié)點(diǎn)正常運(yùn)行,鎖就可以正常運(yùn)行;

6、Redis 中本身沒有對(duì)鎖提供續(xù)期的操作,不過一些第三方的實(shí)現(xiàn)中實(shí)現(xiàn)了 Redis 中鎖的續(xù)期,類似 使用 java 實(shí)現(xiàn)的 Redisson,使用 go 實(shí)現(xiàn)的 redsync,當(dāng)然自己實(shí)現(xiàn)也不是很難,實(shí)現(xiàn)過程可參見上文。

總體來說,Redis 中對(duì)分布式鎖的一些特性都提供了支持,使用 Redis 實(shí)現(xiàn)分布式鎖,是一個(gè)不錯(cuò)的選擇。

分布式鎖如何選擇

1、如果業(yè)務(wù)規(guī)模不大,qps 很小,使用 Redis,etcd,ZooKeeper 去實(shí)現(xiàn)分布式鎖都不會(huì)有問題,就看公司了基礎(chǔ)架構(gòu)了,如果有現(xiàn)成的 Redis,etcd,ZooKeeper 直接用就可以了;

2、Redis 中分布式鎖有一定的安全隱患,如果業(yè)務(wù)中對(duì)安全性要求很高,那么 Redis 可能就不適合了,etcd 或者 ZooKeeper 就比較合適了;

3、如果系統(tǒng) qps 很大,但是可以容忍一些錯(cuò)誤,那么 Redis 可能就更合適了,畢竟 etcd或者ZooKeeper 背面往往都是較低的吞吐量和較高的延遲。

總結(jié)

1、在分布式的場(chǎng)景下,使用分布式鎖是我們經(jīng)常遇到的一種場(chǎng)景;

2、使用 Redis 實(shí)現(xiàn)鎖是個(gè)不錯(cuò)的選擇,Redis 的單命令的執(zhí)行是原子性的同時(shí)借助于 Lua 也可以很容易的實(shí)現(xiàn)組合命令的原子性;

3、針對(duì)分布式場(chǎng)景下主從切換,數(shù)據(jù)同步不及時(shí)的情況,redis 中引入了 redLock 來處理分布式鎖;

4、根據(jù) martin 的描述,redLock 是繁重的,且存在安全性,不過我們可以根據(jù)自己的業(yè)務(wù)場(chǎng)景做出判斷;

5、需要注意的是在設(shè)置分布式鎖的時(shí)候需要設(shè)置 value 的唯一性,并且每次主動(dòng)刪除鎖的時(shí)候需要匹配下 value 的正確性,避免誤刪除其他線程的鎖;

參考

【Redis核心技術(shù)與實(shí)戰(zhàn)】https://time.geekbang.org/column/intro/100056701
【Redis設(shè)計(jì)與實(shí)現(xiàn)】https://book.douban.com/subject/25900156/
【Redis 的學(xué)習(xí)筆記】https://github.com/boilingfrog/Go-POINT/tree/master/redis
【Redis 分布式鎖】https://redis.io/docs/reference/patterns/distributed-locks/
【How to do distributed locking】https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
【etcd 實(shí)現(xiàn)分布式鎖】https://www.cnblogs.com/ricklz/p/15033193.html#分布式鎖
【Redis中的原子操作(3)-使用Redis實(shí)現(xiàn)分布式鎖】https://boilingfrog.github.io/2022/06/15/ Redis中的原子操作 (3)-使用Redis實(shí)現(xiàn)分布式鎖/

到此這篇關(guān)于使用Redis實(shí)現(xiàn)分布式鎖的方法的文章就介紹到這了,更多相關(guān)Redis分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解用Redis實(shí)現(xiàn)Session功能

    詳解用Redis實(shí)現(xiàn)Session功能

    本篇文章主要介紹了用Redis實(shí)現(xiàn)Session功能,具有一定的參考價(jià)值,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。
    2016-12-12
  • Redis突現(xiàn)拒絕連接問題處理方案

    Redis突現(xiàn)拒絕連接問題處理方案

    這篇文章主要介紹了Redis突現(xiàn)拒絕連接問題處理方案,分析原因是由于redis與業(yè)務(wù)共一個(gè)服務(wù)器,內(nèi)存只有8G,業(yè)務(wù)服務(wù)啟動(dòng)過多,內(nèi)存不足導(dǎo)致redis拒絕連接,需要的朋友可以參考下
    2024-02-02
  • Redis 搭建哨兵集群的操作步驟

    Redis 搭建哨兵集群的操作步驟

    在 主從架構(gòu)Redis搭建主從集群 中,一個(gè)slave節(jié)點(diǎn)掛了無影響,但是master節(jié)點(diǎn)掛了,就無法進(jìn)行寫操作了,影響高可用,Redis 提供了哨兵(Sentinel)機(jī)制來實(shí)現(xiàn)主從集群的自動(dòng)故障恢復(fù),本文給大家介紹了Redis 搭建哨兵集群的操作步驟,需要的朋友可以參考下
    2023-08-08
  • redisson滑動(dòng)時(shí)間窗應(yīng)用場(chǎng)景解決方案

    redisson滑動(dòng)時(shí)間窗應(yīng)用場(chǎng)景解決方案

    前10分鐘內(nèi)累計(jì)3次驗(yàn)證失敗后,增加圖形驗(yàn)證碼驗(yàn)證條件,前10分鐘內(nèi)累計(jì)6次驗(yàn)證失敗后,系統(tǒng)自動(dòng)鎖定該賬號(hào)15分鐘,15分鐘后自動(dòng)解鎖,本文給大家分享redisson滑動(dòng)時(shí)間窗應(yīng)用場(chǎng)景解決方案,感興趣的朋友一起看看吧
    2024-01-01
  • Redis Stream類型的使用詳解

    Redis Stream類型的使用詳解

    本文主要介紹了Redis Stream類型的使用詳解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • React?組件的常用生命周期函數(shù)匯總

    React?組件的常用生命周期函數(shù)匯總

    這篇文章主要介紹了React?組件的常用生命周期函數(shù)匯總,組件的生命周期有助于理解組件的運(yùn)行方式、完成更復(fù)雜的組件功能、分析組件錯(cuò)誤原因等
    2022-08-08
  • Spark刪除redis千萬級(jí)別set集合數(shù)據(jù)實(shí)現(xiàn)分析

    Spark刪除redis千萬級(jí)別set集合數(shù)據(jù)實(shí)現(xiàn)分析

    這篇文章主要為大家介紹了Spark刪除redis千萬級(jí)別set集合數(shù)據(jù)實(shí)現(xiàn)過程分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • Windows環(huán)境下Redis Cluster環(huán)境搭建(圖文)

    Windows環(huán)境下Redis Cluster環(huán)境搭建(圖文)

    這篇文章主要介紹了Windows環(huán)境下Redis Cluster環(huán)境搭建(圖文),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • redis啟動(dòng),停止,及端口占用處理方法

    redis啟動(dòng),停止,及端口占用處理方法

    今天小編就為大家分享一篇redis啟動(dòng),停止,及端口占用處理方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • 解決redis在linux上的部署的問題

    解決redis在linux上的部署的問題

    這篇文章主要介紹了redis在linux上的部署,本文分步驟給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02

最新評(píng)論