Inhaltsverzeichnis
Redis Distributed Lock
总结
Heim Datenbank Redis Eingehende Analyse verteilter Sperren in Redis

Eingehende Analyse verteilter Sperren in Redis

Oct 26, 2021 am 10:38 AM
redis 分布式锁

Dieser Artikel führt Sie hauptsächlich in die Implementierung und Codeanalyse verteilter Sperren in Redis ein. Ich hoffe, er wird Ihnen hilfreich sein!

Eingehende Analyse verteilter Sperren in Redis

Redis Distributed Lock

Distributed Lock Handles werden in allen Projekten verwendet. Sie werden normalerweise für ordnungsgemäße Betriebsszenarien von Daten verwendet, z. B. für die Rückerstattung einer Bestellung (wenn diese mehrmals erstattet werden kann). Oder Benutzer geben Bestellungen über mehrere Terminals auf. [Verwandte Empfehlungen: Redis-Video-Tutorial]

Maven-Abhängigkeit

Ich implementieren es hauptsächlich basierend auf Spring-Boot 2.1.2 + JedisSpring-Boot 2.1.2 + Jedis 进行实现

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.2.RELEASE</version>
    </parent>

    <groupId>cn.edu.cqvie</groupId>
    <artifactId>redis-lock</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <redis.version>2.9.0</redis.version>
        <spring-test.version>5.0.7</spring-test.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>${redis.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
Nach dem Login kopieren

配置文件

application.properties 配置文件内容如下:

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.timeout=30000
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.min-idle=2
spring.redis.jedis.pool.max-idle=4


logging.level.root=INFO
Nach dem Login kopieren

接口定义

接口定义,对于锁我们核心其实就连个方法 lockunlock.

public interface RedisLock {

    long TIMEOUT_MILLIS = 30000;

    int RETRY_MILLIS = 30000;

    long SLEEP_MILLIS = 10;

    boolean tryLock(String key);

    boolean lock(String key);

    boolean lock(String key, long expire);

    boolean lock(String key, long expire, long retryTimes);

    boolean unlock(String key);
}
Nach dem Login kopieren

分布式锁实现

我的实现方式是通过 setnx 方式实现了,如果存在 tryLock 逻辑的话,会通过 自旋 的方式重试

// AbstractRedisLock.java 抽象类
public abstract class AbstractRedisLock implements RedisLock {

    @Override
    public boolean lock(String key) {
        return lock(key, TIMEOUT_MILLIS);
    }

    @Override
    public boolean lock(String key, long expire) {
        return lock(key, TIMEOUT_MILLIS, RETRY_MILLIS);
    }
}

// 具体实现
@Component
public class RedisLockImpl extends AbstractRedisLock {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    private ThreadLocal<String> threadLocal = new ThreadLocal<String>();
    private static final String UNLOCK_LUA;

    static {
        StringBuilder sb = new StringBuilder();
        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
        sb.append("then ");
        sb.append("    return redis.call(\"del\",KEYS[1]) ");
        sb.append("else ");
        sb.append("    return 0 ");
        sb.append("end ");
        UNLOCK_LUA = sb.toString();

    }

    @Override
    public boolean tryLock(String key) {
        return tryLock(key, TIMEOUT_MILLIS);
    }

    public boolean tryLock(String key, long expire) {
        try {
            return !StringUtils.isEmpty(redisTemplate.execute((RedisCallback<String>) connection -> {
                JedisCommands commands = (JedisCommands) connection.getNativeConnection();
                String uuid = UUID.randomUUID().toString();
                threadLocal.set(uuid);
                return commands.set(key, uuid, "NX", "PX", expire);
            }));
        } catch (Throwable e) {
            logger.error("set redis occurred an exception", e);
        }
        return false;
    }

    @Override
    public boolean lock(String key, long expire, long retryTimes) {
        boolean result = tryLock(key, expire);

        while (!result && retryTimes-- > 0) {
            try {
                logger.debug("lock failed, retrying...{}", retryTimes);
                Thread.sleep(SLEEP_MILLIS);
            } catch (InterruptedException e) {
                return false;
            }
            result = tryLock(key, expire);
        }
        return result;
    }

    @Override
    public boolean unlock(String key) {
        try {
            List<String> keys = Collections.singletonList(key);
            List<String> args = Collections.singletonList(threadLocal.get());
            Long result = redisTemplate.execute((RedisCallback<Long>) connection -> {
                Object nativeConnection = connection.getNativeConnection();

                if (nativeConnection instanceof JedisCluster) {
                    return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);
                }
                if (nativeConnection instanceof Jedis) {
                    return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);
                }
                return 0L;
            });
            return result != null && result > 0;
        } catch (Throwable e) {
            logger.error("unlock occurred an exception", e);
        }
        return false;
    }
}
Nach dem Login kopieren

测试代码

最后再来看看如何使用吧. (下面是一个模拟秒杀的场景)

@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisLockImplTest {

    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private RedisLock redisLock;
    @Autowired
    private StringRedisTemplate redisTemplate;
    private ExecutorService executors = Executors.newScheduledThreadPool(8);

    @Test
    public void lock() {
        // 初始化库存
        redisTemplate.opsForValue().set("goods-seckill", "10");
        List<Future> futureList = new ArrayList<>();

        for (int i = 0; i < 100; i++) {
            futureList.add(executors.submit(this::seckill));
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 等待结果,防止主线程退出
        futureList.forEach(action -> {
            try {
                action.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });

    }

    public int seckill() {
        String key = "goods";
        try {
            redisLock.lock(key);
            int num = Integer.valueOf(Objects.requireNonNull(redisTemplate.opsForValue().get("goods-seckill")));
            if (num > 0) {
                redisTemplate.opsForValue().set("goods-seckill", String.valueOf(--num));
                logger.info("秒杀成功,剩余库存:{}", num);
            } else {
                logger.error("秒杀失败,剩余库存:{}", num);
            }
            return num;
        } catch (Throwable e) {
            logger.error("seckill exception", e);
        } finally {
            redisLock.unlock(key);
        }
        return 0;
    }
}
Nach dem Login kopieren

总结

本文是 Redis 锁的一种简单的实现方式,基于 jedisrrreee

Konfigurationsdatei

🎜🎜application.properties Der Inhalt der Konfigurationsdatei lautet wie folgt: 🎜rrreee🎜🎜🎜Schnittstellendefinition🎜🎜🎜🎜Schnittstellendefinition, für unsere Kernsperre gibt es eigentlich nur eine Methode lock und unlock > Logik Wenn ja, werden wir es noch einmal über spin versuchen 🎜 Zusammenfassung 🎜🎜🎜Dieser Artikel ist eine einfache Implementierung der Redis-Sperre, die den Sperrwiederholungsvorgang basierend auf jedis implementiert. Es gibt jedoch immer noch Nachteile: Die automatische Erneuerung von Sperren, der erneute Eintritt von Sperren und die Fairness (derzeit durch Spin implementiert, was einer unfairen Methode entspricht) werden nicht unterstützt. 🎜🎜Weitere Kenntnisse zum Thema Programmierung finden Sie unter: 🎜Einführung in die Programmierung🎜! ! 🎜

Das obige ist der detaillierte Inhalt vonEingehende Analyse verteilter Sperren in Redis. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Wie man alles in Myrise freischaltet
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

So erstellen Sie den Redis -Clustermodus So erstellen Sie den Redis -Clustermodus Apr 10, 2025 pm 10:15 PM

Der Redis -Cluster -Modus bietet Redis -Instanzen durch Sharding, die Skalierbarkeit und Verfügbarkeit verbessert. Die Bauschritte sind wie folgt: Erstellen Sie ungerade Redis -Instanzen mit verschiedenen Ports; Erstellen Sie 3 Sentinel -Instanzen, Monitor -Redis -Instanzen und Failover; Konfigurieren von Sentinel -Konfigurationsdateien, Informationen zur Überwachung von Redis -Instanzinformationen und Failover -Einstellungen hinzufügen. Konfigurieren von Redis -Instanzkonfigurationsdateien, aktivieren Sie den Cluster -Modus und geben Sie den Cluster -Informationsdateipfad an. Erstellen Sie die Datei nodes.conf, die Informationen zu jeder Redis -Instanz enthält. Starten Sie den Cluster, führen Sie den Befehl erstellen aus, um einen Cluster zu erstellen und die Anzahl der Replikate anzugeben. Melden Sie sich im Cluster an, um den Befehl cluster info auszuführen, um den Clusterstatus zu überprüfen. machen

Wie wird der Redis -Cluster implementiert? Wie wird der Redis -Cluster implementiert? Apr 10, 2025 pm 05:27 PM

Redis Cluster ist ein verteiltes Bereitstellungsmodell, das die horizontale Expansion von Redis-Instanzen ermöglicht und durch Kommunikation zwischen Noten, Hash-Slot-Abteilung Schlüsselraum, Knotenwahlen, Master-Slave-Replikation und Befehlsumleitung implementiert wird: Inter-Node-Kommunikation: Virtuelle Netzwerkkommunikation wird durch Cluster-Bus realisiert. Hash -Slot: Teilen Sie den Schlüsselraum in Hash -Slots, um den für den Schlüssel verantwortlichen Knoten zu bestimmen. Knotenwahlen: Es sind mindestens drei Master -Knoten erforderlich, und nur ein aktiver Masterknoten wird durch den Wahlmechanismus sichergestellt. Master-Slave-Replikation: Der Masterknoten ist für das Schreiben von Anforderungen verantwortlich und der Slaveknoten ist für das Lesen von Anforderungen und Datenreplikation verantwortlich. Befehlsumleitung: Der Client stellt eine Verbindung zum für den Schlüssel verantwortlichen Knoten her, und der Knoten leitet falsche Anforderungen weiter. Fehlerbehebung: Fehlererkennung, Off-Linie markieren und neu

Wie ist der Schlüssel für die Redis -Abfrage eindeutig Wie ist der Schlüssel für die Redis -Abfrage eindeutig Apr 10, 2025 pm 07:03 PM

Redis verwendet fünf Strategien, um die Einzigartigkeit von Schlüssel zu gewährleisten: 1. Namespace -Trennung; 2. Hash -Datenstruktur; 3.. Datenstruktur festlegen; 4. Sonderzeichen von Stringschlüssel; 5. Lua -Skriptüberprüfung. Die Auswahl spezifischer Strategien hängt von Datenorganisationen, Leistung und Skalierbarkeit ab.

So implementieren Sie die zugrunde liegenden Redis So implementieren Sie die zugrunde liegenden Redis Apr 10, 2025 pm 07:21 PM

Redis verwendet Hash -Tabellen, um Daten zu speichern und unterstützt Datenstrukturen wie Zeichenfolgen, Listen, Hash -Tabellen, Sammlungen und geordnete Sammlungen. Ernähren sich weiterhin über Daten über Snapshots (RDB) und appendiert Mechanismen nur Schreibmechanismen. Redis verwendet die Master-Slave-Replikation, um die Datenverfügbarkeit zu verbessern. Redis verwendet eine Ereignisschleife mit einer Thread, um Verbindungen und Befehle zu verarbeiten, um die Datenatomizität und Konsistenz zu gewährleisten. Redis legt die Ablaufzeit für den Schlüssel fest und verwendet den faulen Löschmechanismus, um den Ablaufschlüssel zu löschen.

So sehen Sie die Versionsnummer der Redis So sehen Sie die Versionsnummer der Redis Apr 10, 2025 pm 05:57 PM

Um die Redis -Versionsnummer anzuzeigen, können Sie die folgenden drei Methoden verwenden: (1) Geben Sie den Info -Befehl ein, (2) Starten Sie den Server mit der Option --version und (3) die Konfigurationsdatei anzeigen.

So sehen Sie alle Schlüssel in Redis So sehen Sie alle Schlüssel in Redis Apr 10, 2025 pm 07:15 PM

Um alle Schlüssel in Redis anzuzeigen, gibt es drei Möglichkeiten: Verwenden Sie den Befehl keys, um alle Schlüssel zurückzugeben, die dem angegebenen Muster übereinstimmen. Verwenden Sie den Befehl scan, um über die Schlüssel zu iterieren und eine Reihe von Schlüssel zurückzugeben. Verwenden Sie den Befehl Info, um die Gesamtzahl der Schlüssel zu erhalten.

So verwenden Sie Redis Zset So verwenden Sie Redis Zset Apr 10, 2025 pm 07:27 PM

Redis bestellte Sets (ZSETs) werden verwendet, um bestellte Elemente und Sortieren nach zugehörigen Bewertungen zu speichern. Die Schritte zur Verwendung von ZSET umfassen: 1. Erstellen Sie ein Zset; 2. Fügen Sie ein Mitglied hinzu; 3.. Holen Sie sich eine Mitgliederbewertung; 4. Holen Sie sich eine Rangliste; 5. Holen Sie sich ein Mitglied in der Rangliste; 6. Ein Mitglied löschen; 7. Holen Sie sich die Anzahl der Elemente; 8. Holen Sie sich die Anzahl der Mitglieder im Score -Bereich.

So verwenden Sie die Befehlszeile der Redis So verwenden Sie die Befehlszeile der Redis Apr 10, 2025 pm 10:18 PM

Verwenden Sie das Redis-Befehlszeilen-Tool (REDIS-CLI), um Redis in folgenden Schritten zu verwalten und zu betreiben: Stellen Sie die Adresse und den Port an, um die Adresse und den Port zu stellen. Senden Sie Befehle mit dem Befehlsnamen und den Parametern an den Server. Verwenden Sie den Befehl Hilfe, um Hilfeinformationen für einen bestimmten Befehl anzuzeigen. Verwenden Sie den Befehl zum Beenden, um das Befehlszeilenwerkzeug zu beenden.

See all articles