diff --git a/common/strmatcher/benchmark_test.go b/common/strmatcher/benchmark_test.go
index 6eb7f0eb..d92ec70c 100644
--- a/common/strmatcher/benchmark_test.go
+++ b/common/strmatcher/benchmark_test.go
@@ -34,3 +34,20 @@ func BenchmarkMarchGroup(b *testing.B) {
 		_ = g.Match("0.v2ray.com")
 	}
 }
+
+func BenchmarkCachedMarchGroup(b *testing.B) {
+	g := NewMatcherGroup()
+	for i := 1; i <= 1024; i++ {
+		m, err := Domain.New(strconv.Itoa(i) + ".v2ray.com")
+		common.Must(err)
+		g.Add(m)
+	}
+
+	cg := NewCachedMatcherGroup(g)
+	_ = cg.Match("0.v2ray.com")
+
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		_ = cg.Match("0.v2ray.com")
+	}
+}
diff --git a/common/strmatcher/strmatcher.go b/common/strmatcher/strmatcher.go
index 15219a0f..a65a68d8 100644
--- a/common/strmatcher/strmatcher.go
+++ b/common/strmatcher/strmatcher.go
@@ -112,7 +112,7 @@ type cacheEntry struct {
 }
 
 type CachedMatcherGroup struct {
-	sync.Mutex
+	sync.RWMutex
 	group   *MatcherGroup
 	cache   map[string]cacheEntry
 	cleanup *task.Periodic
@@ -129,7 +129,7 @@ func NewCachedMatcherGroup(g *MatcherGroup) *CachedMatcherGroup {
 			r.Lock()
 			defer r.Unlock()
 
-			expire := time.Now().Add(-1 * time.Second * 60)
+			expire := time.Now().Add(-1 * time.Second * 120)
 			for p, e := range r.cache {
 				if e.timestamp.Before(expire) {
 					delete(r.cache, p)
@@ -143,22 +143,21 @@ func NewCachedMatcherGroup(g *MatcherGroup) *CachedMatcherGroup {
 }
 
 func (g *CachedMatcherGroup) Match(pattern string) uint32 {
-	g.Lock()
-	defer g.Unlock()
-
+	g.RLock()
 	r, f := g.cache[pattern]
+	g.RUnlock()
 	if f {
-		r.timestamp = time.Now()
-		g.cache[pattern] = r
 		return r.result
 	}
 
 	mr := g.group.Match(pattern)
 
+	g.Lock()
 	g.cache[pattern] = cacheEntry{
 		result:    mr,
 		timestamp: time.Now(),
 	}
+	g.Unlock()
 
 	return mr
 }