为什么在锁定的ActiveRecord查询生成的SQL中看不到“ FOR UPDATE”?

时间:2018-11-04 03:07:35

标签: sql ruby-on-rails rails-activerecord pessimistic-locking

我正在阅读“ The Rails 5 Way”,在第191页上,我看到以下内容:

  

悲观锁定发生在数据库级别。选择   Active Record生成的语句将具有FOR UPDATE(或   相似)子句添加到其中...

Rails docs似乎包含相同的信息:

  

Locking :: Pessimistic使用以下命令支持行级锁定   选择…FOR UPDATE和其他锁定类型。

     

将ActiveRecord :: Base#绑定到ActiveRecord :: QueryMethods#lock   在选定的行上获得排他锁:

     

Account.lock.find(1)#选择*从ID = 1的帐户进行更新

作为一个实验,我想在本地计算机上重现此FOR UPDATE语句。我知道使用悲观锁定启动事务的方法是调用.lock类方法(本书给出了示例t = Timesheet.lock.first)。因此,我在玩具Rails应用(v 5.1.6)的REPL中运行了以下代码,其中包含Order类:

irb(main):015:0> Order.transaction do       
irb(main):016:1* o1 = Order.lock.first
irb(main):017:1> o1.update_attributes(name: 'Foo Bar')
irb(main):018:1> end

这产生了以下输出:

(0.3ms)  begin transaction
Order Load (0.2ms)  SELECT  "orders".* FROM "orders" ORDER BY "orders"."id" ASC LIMIT ?   [["LIMIT", 1]]
SQL (1.1ms)  UPDATE "orders" SET "name" = ?, "updated_at" = ? WHERE "orders"."id" = ?  [["name", "Foo Bar"], ["updated_at", "2018-11-04 03:01:35.593868"], ["id", 1]]
(0.4ms)  commit transaction
=> true

FOR UPDATESELECT语句中都没有看到UPDATE。尝试指定悲观锁定时,我做错什么了吗?还是我对应该输出什么SQL有不正确的期望?

1 个答案:

答案 0 :(得分:0)

我发现我的玩具应用程序正在使用默认的Rails #include <iostream> #include <cmath> const double degToRad = std::acos(-1) / 180; struct vec3 { double x, y, z; vec3(double xd, double yd, double zd) : x(xd), y(yd), z(zd) {} double length() { return std::sqrt(x*x + y*y + z*z); } void normalize() { double len = length(); x = x / len; y = y / len; z = z / len; } }; vec3 cross(const vec3& v1, const vec3& v2) { return vec3( v1.y * v2.z - v2.y * v1.z, v1.z * v2.x - v2.z * v1.x, v1.x * v2.y - v2.x * v1.y ); } double dot(const vec3& v1, const vec3& v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } double GCDistance(const vec3& v1, const vec3& v2, double R) { //normalize, so we can pass any vectors vec3 v1n = v1; v1n.normalize(); vec3 v2n = v2; v2n.normalize(); vec3 tmp = cross(v1n, v2n); //minimum distance may be in one direction or the other double d1 = std::abs(R * std::atan2(tmp.length() , dot(v1n, v2n))); double d2 = std::abs(R * std::atan2(tmp.length() , -dot(v1n, v2n))); return std::min(std::abs(d1), std::abs(d2)); } int main() { //Points A, B, and P double lon1 = 88.41253929999999 * degToRad; double lat1 = 22.560206299999997 * degToRad; double lon2 = 88.36928063300775 * degToRad; double lat2 = 22.620867969497795 * degToRad; double lon3 = 88.29580956367181 * degToRad; double lat3 = 22.71558662052875 * degToRad; //Let's work with a sphere of R = 1 vec3 OA(std::cos(lat1) * std::cos(lon1), std::cos(lat1) * std::sin(lon1), std::sin(lat1)); vec3 OB(std::cos(lat2) * std::cos(lon2), std::cos(lat2) * std::sin(lon2), std::sin(lat2)); vec3 OP(std::cos(lat3) * std::cos(lon3), std::cos(lat3) * std::sin(lon3), std::sin(lat3)); //plane OAB, defined by its perpendicular vector pp1 vec3 pp1 = cross(OA, OB); //plane OPC vec3 pp2 = cross(pp1, OP); //planes intersection, defined by a line whose vector is ppi vec3 ppi = cross(pp1, pp2); ppi.normalize(); //unitary vector //Radious or Earth double R = 6371000; //mean value. For more precision, data from a reference ellipsoid is required std::cout << "Distance AP = " << GCDistance(OA, OP, R) << std::endl; std::cout << "Distance BP = " << GCDistance(OB, OP, R) << std::endl; std::cout << "Perpendicular distance (on arc) = " << GCDistance(OP, ppi, R) << std::endl; } 数据库。我创建了一个新的玩具应用(sqlite),创建了具有多个实例的新用户模型,并运行了rails new newbie --database=postgresql,然后看到了以下内容:

User.lock.first

如您所见,irb(main):004:0> User.lock.first User Load (1.7ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 FOR UPDATE [["LIMIT", 1]] => #<User id: 1, name: nil, phone: nil, created_at: "2018-11-05 01:28:23", updated_at: "2018-11-05 01:28:23"> 出现在SQL查询中。从this Stack Overflow answer中,我看到默认情况下SQLite不支持悲观锁定:

  不支持

SELECT ... FOR UPDATE OF...。这是可以理解的   考虑到SQLite的机制,行锁定是多余的   因为在更新数据库的任何位时,整个数据库都被锁定了。