【问题标题】:Cache Slick DBIO Actions缓存 Slick DBIO 操作
【发布时间】:2017-05-29 15:56:15
【问题描述】:

我正在尝试加快“SELECT * FROM WHERE name=?”的速度Play 中的各种查询! + 斯卡拉应用程序。我正在使用 Play 2.4 + Scala 2.11 + play-slick-1.1.1 包。此包使用 Slick-3.1 版本。

我的假设是 slick 从 DBIO 操作生成 Prepared 语句并执行它们。所以我尝试缓存他们购买开启标志 cachePrepStmts=true 但是我仍然在日志中看到“Preparing statement...”消息,这意味着 PS 没有被缓存!应该如何指示 slick 缓存它们?

如果我运行以下代码,PS 不应该在某个时候被缓存吗?

for (i <- 1 until 100) {
  Await.result(db.run(doctorsTable.filter(_.userName === name).result), 10 seconds)
}

Slick 配置如下:

slick.dbs.default {
  driver="slick.driver.MySQLDriver$"
  db {
    driver="com.mysql.jdbc.Driver"

    url="jdbc:mysql://localhost:3306/staging_db?useSSL=false&cachePrepStmts=true"

    user = "user"

    password = "passwd"

    numThreads = 1  // For not just one thread in HikariCP

    properties = {
      cachePrepStmts = true
      prepStmtCacheSize = 250
      prepStmtCacheSqlLimit = 2048
    }
  }

}

更新 1

我按照@pawel 的使用编译查询的建议尝试了以下操作:

val compiledQuery = Compiled { name: Rep[String] =>
  doctorsTable.filter(_.userName === name)
}


val stTime = TimeUtil.getUtcTime
for (i <- 1 until 100) {
  FutureUtils.blockFuture(db.compiledQuery(name).result), 10)
}
val endTime = TimeUtil.getUtcTime - stTime
Logger.info(s"Time Taken HERE $endTime")

在我的日志中,我仍然看到如下语句:

2017-01-16 21:34:00,510 DEBUG [db-1] s.j.J.statement [?:?] Preparing statement: select ...

这个时间也是一样的。期望的输出是什么?我应该不再看到这些陈述吗?如何验证 Prepared 语句是否确实被重用。

【问题讨论】:

    标签: scala playframework slick hikaricp play-slick


    【解决方案1】:

    您需要使用 Compiled 查询 - 这正是您想要的。

    只需将上面的代码更改为:

    val compiledQuery = Compiled { name: Rep[String] =>
        doctorsTable.filter(_.userName === name)
    }
    
    for (i <- 1 until 100) {
      Await.result(db.run(compiledQuery(name).result), 10 seconds)
    }
    

    我将上面的name 提取为参数(因为您通常想更改PreparedStatements 中的一些参数)但这绝对是一个可选部分。

    更多信息您可以参考:http://slick.lightbend.com/doc/3.1.0/queries.html#compiled-queries

    【讨论】:

    • 啊哈!让我检查一下,然后回复你。感谢这个非常有用的观察 :) 顺便问一下,我的 slick 配置中有任何 cmets 吗?既然我指定了 JDBC url,你认为 properties 对象是多余的吗?
    • 我不确定它是否有效!我在有和没有编译查询的情况下对这个循环进行了计时,结果几乎相同。我看到来自此文件的Preparing statement: select user_name, ... 消息:github.com/slick/slick/blob/… 我如何确保准备好的语句实际上被重用?
    • 你确定你有val compiledQuery而不是def compiledQuery
    • 当然是val。你知道我想看看什么样的日志可以按预期工作吗?
    • 之前肯定有一些错误,现在在服务器端日志记录和配置参数更加清晰之后,我可以看到编译查询更快。感谢您的帮助!
    【解决方案2】:

    对于 MySQL,您需要设置一个额外的 jdbc 标志,useServerPrepStmts=true

    HikariCP 的 MySQL configuration page 链接到一个非常有用的文档,该文档为 MySQL jdbc 提供了一些简单的 performance tuning 配置选项。

    这里有一些我发现有用的(你需要&amp; 将它们附加到 jdbc url 以获得 Hikari 的 API 未公开的选项)。请务必阅读每个选项的链接文档和/或 MySQL 文档;应该大部分可以安全使用。

    zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8
    rewriteBatchedStatements=true
    maintainTimeStats=false
    cacheServerConfiguration=true
    avoidCheckOnDuplicateKeyUpdateInSQL=true
    dontTrackOpenResources=true
    useLocalSessionState=true
    cachePrepStmts=true
    useServerPrepStmts=true
    prepStmtCacheSize=500
    prepStmtCacheSqlLimit=2048
    

    另外,请注意语句是按线程缓存的;根据您为 Hikari 连接 maxLifetime 设置的内容以及服务器负载,服务器和客户端的内存使用量将相应增加(例如,如果您将连接最大生命周期设置为略低于 MySQL 默认的 8 小时,服务器和客户端都将保持N 个准备好的语句在每个连接的生命周期内都在内存中存活)。

    附言好奇瓶颈是否确实是语句缓存或 Slick 特有的东西。

    编辑

    to log statements 启用查询日志。在 MySQL 5.7 上,您将添加到您的 my.cnf:

    general-log=1
    general-log-file=/var/log/mysqlgeneral.log
    

    然后sudo touch /var/log/mysqlgeneral.log 然后重新启动mysqld。注释掉上面的配置行并重新启动以关闭查询日志记录。

    【讨论】:

    • 感谢您的回复。目前,我完全好奇准备好的语句缓存是否在 Slick + Hikari 组合中有效。我只是想知道在 JDBC 中如何验证preparedstatement 是否被缓存?
    • 启用服务器端语句缓存后,mysql 查询日志将显示单个准备 + 执行初始查询,然后仅针对目标语句的所有后续缓存命中执行。如果您添加 useServerPrepStmts=true,您应该会在日志中看到此模式。
    • 我试过了。其实你知道我怎样才能打开mysql查询日志吗?我打开了流畅的日志记录,无论如何它总是显示Preparing statement: select ... 类型的日志。这条语句来自 JdbcBackend.scala:github.com/slick/slick/blob/… 这样会造成混乱
    • 非常感谢!我现在可以看到准备语句在起作用。有趣的是,仅使用编译查询而不使用任何服务器端缓存实际上快了 9% 到 11%
    猜你喜欢
    • 2019-04-03
    • 1970-01-01
    • 2021-04-18
    • 2017-06-08
    • 2014-11-18
    • 2014-06-14
    • 2015-05-16
    • 2010-10-16
    • 1970-01-01
    相关资源
    最近更新 更多