我正在阅读“ 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 UPDATE
或SELECT
语句中都没有看到UPDATE
。尝试指定悲观锁定时,我做错什么了吗?还是我对应该输出什么SQL有不正确的期望?
答案 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的机制,行锁定是多余的 因为在更新数据库的任何位时,整个数据库都被锁定了。