Golang Go语言中的 select 方法很适合 cache 啊

发布于 1周前 作者 yuanlaile 来自 Go语言

在这里看到的。https://mzh.io/%E4%B8%80%E4%BA%9BGolang%E5%B0%8F%E6%8A%80%E5%B7%A7

在 nsq 中,需要读取之前磁盘上的,或者是从内存中直接读取,一般人都是先判断内存中有没有数据,然而,nsq 另辟蹊径使用了 select 语句,把 CSP 模式用到了极致。

select {
        case msg = <-c.memoryMsgChan:  //尝试从内存中读取
        case buf = <-c.backend.ReadChan(): //如果内存中没有,直接从磁盘上读取
            msg, err = decodeMessage(buf)
            if err != nil {
                c.ctx.nsqd.logf("ERROR: failed to decode message - %s", err)
                continue
            }

这个想法蛮好啊。


Golang Go语言中的 select 方法很适合 cache 啊

更多关于Golang Go语言中的 select 方法很适合 cache 啊的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

26 回复

go 是最好的语言,go 实现的网站
www.cshome.com

更多关于Golang Go语言中的 select 方法很适合 cache 啊的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这样写确实挺简洁的,比我的 if else 短一些。

你需要多开 channel 和 goroutine,这是占一些资源的 不过可以忽略。go 语言级别支持 csp,go 的 select 是在用户态中进行对 goroutine 的调度。

但是从 channel 中取出后,channel 中的消息已经没有了,这样还需要在消费后再将消息推送进 channel,并且还要自己维护失效时间。

乍一看域名以为是游戏相关的网站。。。

对的 这个是要自己再搞下。

go 并没有承诺 select 前面的高优先级,实测也发现并不是按照顺序确定优先级,而是乱序。

<br><br>package main<br><br>import (<br> "fmt"<br> "time"<br>)<br><br>func main() {<br><br> c1 := make(chan int, 10)<br> c2 := make(chan int, 10)<br><br> go func() {<br> for i := 0; i &lt; 100; i++ {<br> c1 &lt;- 1<br> }<br> }()<br> go func() {<br> for i := 0; i &lt; 100; i++ {<br> c2 &lt;- 2<br> }<br> }()<br><br> time.Sleep(100 * time.Millisecond)<br><br> for {<br> select {<br> case i := &lt;-c1:<br> fmt.Println(i)<br> case i := &lt;-c2:<br> fmt.Println(i)<br> }<br> }<br><br>}<br><br>

输出结果:
1
2
2
2
1
2
1
1
2
1
2
2
2
1
2
1
2
2
2
2
1
1
2
1
2
1
2
1
2
1
2
2
2
2
1
1
1
2
1
2
2
1
1
2
1
1
2
2
2
1
1
2
2
2
1
2
1
2
1
2
2
2
1

慎用,case 后面的都会被执行,本质上是在比哪个 case 更快返回

func TestSelectCase(t *testing.T) {

var fromCache = func() chan int {
t.Log(“call fromCache()”)
c := make(chan int, 1)

<-time.After(time.Second * 1)
c <- 1

return c
}

var fromDB = func() chan int {
t.Log(“call fromDB()”)
c := make(chan int, 1)

<-time.After(time.Second * 2)
c <- 1

return c
}

select {
case <-fromCache():
t.Log(“got from cache”)
case <-fromDB():
t.Log(“got from db”)
}
}

select_case_test.go:11: call fromCache()
select_case_test.go:21: call fromDB()
select_case_test.go:34: got from db


/////////////////////////

func TestSelectCase(t *testing.T) {

var fromCache = func() chan int {
t.Log(“call fromCache()”)
c := make(chan int, 1)

go func() {
<-time.After(time.Second * 1)
c <- 1
}()

return c
}

var fromDB = func() chan int {
t.Log(“call fromDB()”)
c := make(chan int, 1)

go func() {
<-time.After(time.Second * 2)
c <- 1
}()

return c
}

select {
case <-fromCache():
t.Log(“got from cache”)
case <-fromDB():
t.Log(“got from db”)
}
}

select_case_test.go:11: call fromCache()
select_case_test.go:23: call fromDB()
select_case_test.go:36: got from cache

你给一个页面怎么看出来是 go 实现的

所以 select 还要考虑 switch 的条件,case 的条件同一时刻只满足一个,如果同时满足多个 调度器选择执行哪一个都是有可能的

所以我说感觉就是这个场景的(缓存)下,这种设计还是很精妙的。

学到了,golang 文档好像没有写明

有弊端。 当缓存和硬盘中都有数据的时候, 理论上是想从缓存读取,这样速度快,才起到缓存的作用,而却从硬盘取数据了,这样即使有缓存数据也没有起到作用。 所以 这种写法只适合非常简单的一种缓存机制,即使从硬盘取也不会很慢。 真要做大规模的缓存,不适合吧,当高并发的时候,会太多缓存失效的情况会发生,而实际中这些缓存数据都是有的

这样做的前提是读压力小,读压力大的话,磁盘 io 直接就爆了

这代码逻辑有问题啊,有可能在有缓存的时候读盘,吹 go 不是这么吹的

这里也有讲的。

唔~ 有道理~

读盘理论上应该比返回慢吧~读缓存应该先返回了

这个是看哪个 case 运气好被执行了

这个域名不做 CSGO 社区可以了……

select 的一个神坑就是多个 case 之间是无序的,当都有数值的时候会随机的返回其中一个 case,巨恶心。

代码逻辑没问题,建议看完整的代码。你贴的代码里那段中文注释错了,这里不是 if else,是 or 的关系。select 本身是无序的。

说 select-case 无序是坑的,官方文档早就说明得很详细了,而且这特性可以实现一些比较有意思的功能。
敢问坑在哪里?

两个都是 channel,应该是谁刚好有数据就选谁。channel 不可能主动去查磁盘吧?应该要别的 goroutine 从磁盘查数据放进 channel。

在Go语言中,select语句确实非常适合用于处理缓存(cache)相关的并发场景。select语句可以监听多个通信操作(如发送和接收)并在其中一个操作可以立即进行时执行它,这使得它成为处理缓存读取和写入时的理想选择。

缓存通常需要在多个goroutine之间共享,以支持并发的读写操作。通过使用select语句,我们可以优雅地处理以下几种情况:

  1. 缓存命中:当缓存中有数据时,我们可以立即从缓存中读取数据,而无需等待或阻塞。

  2. 缓存未命中:当缓存中没有数据时,我们可以使用select语句来监听从其他来源(如数据库或远程服务)获取数据的通道。一旦数据可用,select语句将自动选择该通道并处理数据,然后将其存入缓存以供后续使用。

  3. 超时处理:在访问缓存时,我们可能希望设置一个超时时间,以避免长时间的等待。select语句可以很容易地实现这一点,通过监听一个带有超时的通道来触发超时处理逻辑。

总之,select语句在Go语言中提供了一种强大且灵活的方式来处理并发通信,这使得它成为实现高效缓存系统的关键工具。通过合理地使用select语句,我们可以构建出既能够高效处理并发读写操作,又能够优雅地处理各种异常情况(如缓存未命中和超时)的缓存系统。

回到顶部