From 90f6ddfe447eeff9756dbf2bf88921b8d020af99 Mon Sep 17 00:00:00 2001 From: wangxiao1024 <10048976+wangxiao1024@user.noreply.gitee.com> Date: Wed, 24 Jul 2024 14:05:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=96=B9=E4=BE=BF=E5=88=9D=E5=AD=A6=E8=80=85?= =?UTF-8?q?=E5=AD=A6=E4=B9=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- datastruct/dict/concurrent.go | 8 ++++++ datastruct/dict/dict.go | 24 +++++++++--------- datastruct/lock/lock_map.go | 47 +++++++++++++++++++++++------------ 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/datastruct/dict/concurrent.go b/datastruct/dict/concurrent.go index 74302da..88824d5 100644 --- a/datastruct/dict/concurrent.go +++ b/datastruct/dict/concurrent.go @@ -25,6 +25,7 @@ type shard struct { } // computeCapacity 计算哈希表容量,确保为2的次幂,这有助于更快的计算索引 +// 这个函数的目的是确定一个接近于给定参数 param 但不小于它的2的次幂的数。这样做的好处主要有两点: func computeCapacity(param int) (size int) { // 以下代码是经典的位操作技巧,用于找到大于等于param的最小2的次幂 if param <= 16 { @@ -82,6 +83,9 @@ func (dict *ConcurrentDict) spread(hashCode uint32) uint32 { panic("dict is nil") } tableSize := uint32(len(dict.table)) + + //因为表的大小是2的次幂,这个按位与操作等同于对表大小取模,但运算速度更快。 + //当表大小是2的次幂时,tableSize - 1 的二进制表示中所有低位都是1,这使得按位与操作只保留 hashCode 的最低的几位。 return (tableSize - 1) & hashCode } @@ -459,6 +463,10 @@ func (dict *ConcurrentDict) RWUnLocks(writeKeys []string, readKeys []string) { } } } + +// stringsToBytes 将字符串切片转换为字节切片的切片。 +// 输入:strSlice - 字符串切片 +// 输出:每个字符串转换为字节切片后的切片 func stringsToBytes(strSlice []string) [][]byte { byteSlice := make([][]byte, len(strSlice)) for i, str := range strSlice { diff --git a/datastruct/dict/dict.go b/datastruct/dict/dict.go index 4fdf279..9eb6653 100644 --- a/datastruct/dict/dict.go +++ b/datastruct/dict/dict.go @@ -1,20 +1,20 @@ package dict -// Consumer is used to traversal dict, if it returns false the traversal will be break +// Consumer 是一个遍历字典时使用的函数类型,如果返回false,则终止遍历 type Consumer func(key string, val interface{}) bool -// Dict is interface of a key-value data structure +// Dict 是 key-value 数据结构的接口定义 type Dict interface { - Get(key string) (val interface{}, exists bool) - Len() int - Put(key string, val interface{}) (result int) - PutIfAbsent(key string, val interface{}) (result int) - PutIfExists(key string, val interface{}) (result int) - Remove(key string) (val interface{}, result int) - ForEach(consumer Consumer) - Keys() []string - RandomKeys(limit int) []string + Get(key string) (val interface{}, exists bool) // 获取键的值 + Len() int // 返回字典的长度 + Put(key string, val interface{}) (result int) // 插入键值对 + PutIfAbsent(key string, val interface{}) (result int) // 如果键不存在,则插入 + PutIfExists(key string, val interface{}) (result int) // 如果键存在,则插入 + Remove(key string) (val interface{}, result int) // 移除键 + ForEach(consumer Consumer) // 遍历字典 + Keys() []string // 返回所有键的列表 + RandomKeys(limit int) []string // 随机返回一定数量的键 RandomDistinctKeys(limit int) []string - Clear() + Clear() // 清除字典中的所有元素 DictScan(cursor int, count int, pattern string) ([][]byte, int) } diff --git a/datastruct/lock/lock_map.go b/datastruct/lock/lock_map.go index 72fd096..c6d05b4 100644 --- a/datastruct/lock/lock_map.go +++ b/datastruct/lock/lock_map.go @@ -5,16 +5,29 @@ import ( "sync" ) +/* +即使使用了 ConcurrentMap 保证了对单个 key 的并发安全性,但在某些情况下这还不够。 +例如,Incr 命令需要完成从读取到修改再到写入的一系列操作,而 MSETNX 命令需要在所有给定键都不存在的情况下才设置值。 +这些操作需要更复杂的并发控制,即锁定多个键直到操作完成。 +*/ + +/* +锁定哈希槽而非单个键:通常,每个键都需要独立锁定来确保并发访问的安全性。 +但这在键数量很大时会造成大量内存的使用。通过锁定哈希槽,可以大大减少所需的锁的数量,因为多个键会映射到同一个哈希槽。 +避免内存泄漏:如果为每个键分配一个锁,随着键的增加和删除,锁的数量也会不断增加,可能会造成内存泄漏。 +锁定哈希槽可以固定锁的数量,即使在键的数量动态变化时也不会增加额外的内存负担。 +*/ +// 使用固定素数,用于FNV哈希函数 const ( prime32 = uint32(16777619) ) -// Locks provides rw locks for key +// Locks结构体包含了一个用于读写锁定的锁表 type Locks struct { table []*sync.RWMutex } -// Make creates a new lock map +// Make 初始化并返回一个具有指定数量哈希槽的Locks实例 func Make(tableSize int) *Locks { table := make([]*sync.RWMutex, tableSize) for i := 0; i < tableSize; i++ { @@ -25,6 +38,7 @@ func Make(tableSize int) *Locks { } } +// fnv32 实现了FNV哈希算法,用于生成键的哈希值 func fnv32(key string) uint32 { hash := uint32(2166136261) for i := 0; i < len(key); i++ { @@ -34,6 +48,7 @@ func fnv32(key string) uint32 { return hash } +// spread 计算给定哈希码的哈希槽索引 func (locks *Locks) spread(hashCode uint32) uint32 { if locks == nil { panic("dict is nil") @@ -42,36 +57,37 @@ func (locks *Locks) spread(hashCode uint32) uint32 { return (tableSize - 1) & hashCode } -// Lock obtains exclusive lock for writing +// Lock 为给定键获取独占写锁 func (locks *Locks) Lock(key string) { index := locks.spread(fnv32(key)) mu := locks.table[index] mu.Lock() } -// RLock obtains shared lock for reading +// RLock 为给定键获取共享读锁 func (locks *Locks) RLock(key string) { index := locks.spread(fnv32(key)) mu := locks.table[index] mu.RLock() } -// UnLock release exclusive lock +// UnLock 释放给定键的独占写锁 func (locks *Locks) UnLock(key string) { index := locks.spread(fnv32(key)) mu := locks.table[index] mu.Unlock() } -// RUnLock release shared lock +// RUnLock 释放给定键的共享读锁 func (locks *Locks) RUnLock(key string) { index := locks.spread(fnv32(key)) mu := locks.table[index] mu.RUnlock() } +// toLockIndices 计算一组键的哈希槽索引,并进行排序(可选逆序) func (locks *Locks) toLockIndices(keys []string, reverse bool) []uint32 { - indexMap := make(map[uint32]struct{}) + indexMap := make(map[uint32]struct{}) // 使用集合去重 for _, key := range keys { index := locks.spread(fnv32(key)) indexMap[index] = struct{}{} @@ -89,18 +105,17 @@ func (locks *Locks) toLockIndices(keys []string, reverse bool) []uint32 { return indices } -// Locks obtains multiple exclusive locks for writing -// invoking Lock in loop may cause deadlock, please use Locks +// Locks 获取多个键的独占写锁,避免死锁 func (locks *Locks) Locks(keys ...string) { indices := locks.toLockIndices(keys, false) for _, index := range indices { mu := locks.table[index] - mu.Lock() + mu.Lock() // 锁定每个索引对应的哈希槽 } } -// RLocks obtains multiple shared locks for reading -// invoking RLock in loop may cause deadlock, please use RLocks +// RLocks 获取多个键的共享读锁,避免死锁 + func (locks *Locks) RLocks(keys ...string) { indices := locks.toLockIndices(keys, false) for _, index := range indices { @@ -109,7 +124,7 @@ func (locks *Locks) RLocks(keys ...string) { } } -// UnLocks releases multiple exclusive locks +// UnLocks 释放多个键的独占写锁 func (locks *Locks) UnLocks(keys ...string) { indices := locks.toLockIndices(keys, true) for _, index := range indices { @@ -118,7 +133,7 @@ func (locks *Locks) UnLocks(keys ...string) { } } -// RUnLocks releases multiple shared locks +// RUnLocks 释放多个键的共享读锁 func (locks *Locks) RUnLocks(keys ...string) { indices := locks.toLockIndices(keys, true) for _, index := range indices { @@ -127,7 +142,7 @@ func (locks *Locks) RUnLocks(keys ...string) { } } -// RWLocks locks write keys and read keys together. allow duplicate keys +// RWLocks 同时获取写锁和读锁,允许键重复 func (locks *Locks) RWLocks(writeKeys []string, readKeys []string) { keys := append(writeKeys, readKeys...) indices := locks.toLockIndices(keys, false) @@ -147,7 +162,7 @@ func (locks *Locks) RWLocks(writeKeys []string, readKeys []string) { } } -// RWUnLocks unlocks write keys and read keys together. allow duplicate keys +// RWUnLocks 同时释放写锁和读锁,允许键重复 func (locks *Locks) RWUnLocks(writeKeys []string, readKeys []string) { keys := append(writeKeys, readKeys...) indices := locks.toLockIndices(keys, true)