当前位置: 首页 > news >正文

设计师网站官网电子商务网站开发项目

设计师网站官网,电子商务网站开发项目,网站后台管理系统怎么登陆,莱芜金点子广告最新招聘信息为了确保连续多个操作的原子性,一个成熟的数据库通常都会有事务支持,Redis 也不例外。Redis 的事务使用非常简单,不同于关系数据库,我们无须理解那么多复杂的事务模型,就可以直接使用。不过也正是因为这种简单性&#…

为了确保连续多个操作的原子性,一个成熟的数据库通常都会有事务支持,Redis 也不例外。Redis 的事务使用非常简单,不同于关系数据库,我们无须理解那么多复杂的事务模型,就可以直接使用。不过也正是因为这种简单性,它的事务模型很不严格,这要求我们不能像使用关系数据库的事务一样来使用 Redis。

Redis 事务的基本使用

每个事务的操作都有 begin、commit 和 rollback,begin 指示事务的开始,commit 指示事务的提交,rollback 指示事务的回滚。它大致的形式如下。

java复制代码begin();
try {
    command1();
    command2();
    ....
    commit();
catch(Exception e) {
    rollback();
}

Redis 在形式上看起来也差不多,分别是 multi/exec/discard。multi 指示事务的开始,exec 指示事务的执行,discard 指示事务的丢弃。

shell复制代码> multi
OK
> incr books
QUEUED
> incr books
QUEUED
> exec
(integer) 1
(integer) 2

上面的指令演示了一个完整的事务过程,所有的指令在 exec 之前不执行,而是缓存在服务器的一个事务队列中,服务器一旦收到 exec 指令,才开执行整个事务队列,执行完毕后一次性返回所有指令的运行结果。因为 Redis 的单线程特性,它不用担心自己在执行队列的时候被其它指令打搅,可以保证他们能得到的「原子性」执行。

img 上图显示了以上事务过程完整的交互效果。QUEUED 是一个简单字符串,同 OK 是一个形式,它表示指令已经被服务器缓存到队列里了。

原子性

事务的原子性是指要么事务全部成功,要么全部失败,那么 Redis 事务执行是原子性的么?

下面我们来看一个特别的例子。

shell复制代码> multi
OK
> set books iamastring
QUEUED
> incr books
QUEUED
> set poorman iamdesperate
QUEUED
> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
> get books
"iamastring"
>  get poorman
"iamdesperate

上面的例子是事务执行到中间遇到失败了,因为我们不能对一个字符串进行数学运算,事务在遇到指令执行失败后,后面的指令还继续执行,所以 poorman 的值能继续得到设置。

到这里,你应该明白 Redis 的事务根本不能算「原子性」,而仅仅是满足了事务的「隔离性」,隔离性中的串行化——当前执行的事务有着不被其它事务打断的权利。

discard(丢弃)

Redis 为事务提供了一个 discard 指令,用于丢弃事务缓存队列中的所有指令,在 exec 执行之前。

markdown复制代码> get books
(nil)
> multi
OK
> incr books
QUEUED
> incr books
QUEUED
> discard
OK
> get books
(nil)

我们可以看到 discard 之后,队列中的所有指令都没执行,就好像 multi 和 discard 中间的所有指令从未发生过一样。

优化

上面的 Redis 事务在发送每个指令到事务缓存队列时都要经过一次网络读写,当一个事务内部的指令较多时,需要的网络 IO 时间也会线性增长。所以通常 Redis 的客户端在执行事务时都会结合 pipeline 一起使用,这样可以将多次 IO 操作压缩为单次 IO 操作。比如我们在使用 Python 的 Redis 客户端时执行事务时是要强制使用 pipeline 的。

py复制代码pipe = redis.pipeline(transaction=true)
pipe.multi()
pipe.incr("books")
pipe.incr("books")
values = pipe.execute()

Watch

考虑到一个业务场景,Redis 存储了我们的账户余额数据,它是一个整数。现在有两个并发的客户端要对账户余额进行修改操作,这个修改不是一个简单的 incrby 指令,而是要对余额乘以一个倍数。Redis 可没有提供 multiplyby 这样的指令。我们需要先取出余额然后在内存里乘以倍数,再将结果写回 Redis。

这就会出现并发问题,因为有多个客户端会并发进行操作。我们可以通过 Redis 的分布式锁来避免冲突,这是一个很好的解决方案。分布式锁是一种悲观锁,那是不是可以使用乐观锁的方式来解决冲突呢?

Redis 提供了这种 watch 的机制,它就是一种乐观锁。有了 watch 我们又多了一种可以用来解决并发修改的方法。 watch 的使用方式如下:

py复制代码while True:
    do_watch()
    commands()
    multi()
    send_commands()
    try:
        exec()
        break
    except WatchError:
        continue

watch 会在事务开始之前盯住 1 个或多个关键变量,当事务执行时,也就是服务器收到了 exec 指令要顺序执行缓存的事务队列时,Redis 会检查关键变量自 watch 之后,是否被修改了 (包括当前事务所在的客户端)。如果关键变量被人动过了,exec 指令就会返回 null 回复告知客户端事务执行失败,这个时候客户端一般会选择重试。

shell复制代码> watch books
OK
> incr books  # 被修改了
(integer) 1
> multi
OK
> incr books
QUEUED
> exec  # 事务执行失败
(nil)

当服务器给 exec 指令返回一个 null 回复时,客户端知道了事务执行是失败的,通常客户端 (redis-py) 都会抛出一个 WatchError 这种错误,不过也有些语言 (jedis) 不会抛出异常,而是通过在 exec 方法里返回一个 null,这样客户端需要检查一下返回结果是否为 null 来确定事务是否执行失败。

注意事项

Redis 禁止在 multi 和 exec 之间执行 watch 指令,而必须在 multi 之前做好盯住关键变量,否则会出错。

接下来我们使用 Python 语言来实现对余额的加倍操作。

py复制代码# -*- coding: utf-8
import redis

def key_for(user_id):
    return "account_{}".format(user_id)

def double_account(client, user_id):
    key = key_for(user_id)
    while True:
        pipe = client.pipeline(transaction=True)
        pipe.watch(key)
        value = int(pipe.get(key))
        value *= 2  # 加倍
        pipe.multi()
        pipe.set(key, value)
        try:
            pipe.execute()
            break  # 总算成功了
        except redis.WatchError:
            continue  # 事务被打断了,重试
    return int(client.get(key))  # 重新获取余额

client = redis.StrictRedis()
user_id = "abc"
client.setnx(key_for(user_id), 5)  # setnx 做初始化
print double_account(client, user_id)

下面我们再使用 Java 语言实现一遍。

java复制代码import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class TransactionDemo {

  public static void main(String[] args) {
    Jedis jedis = new Jedis();
    String userId = "abc";
    String key = keyFor(userId);
    jedis.setnx(key, String.valueOf(5));  # setnx 做初始化
    System.out.println(doubleAccount(jedis, userId));
    jedis.close();
  }

  public static int doubleAccount(Jedis jedis, String userId) {
    String key = keyFor(userId);
    while (true) {
      jedis.watch(key);
      int value = Integer.parseInt(jedis.get(key));
      value *= 2// 加倍
      Transaction tx = jedis.multi();
      tx.set(key, String.valueOf(value));
      List<Object> res = tx.exec();
      if (res != null) {
        break// 成功了
      }
    }
    return Integer.parseInt(jedis.get(key)); // 重新获取余额
  }

  public static String keyFor(String userId) {
    return String.format("account_%s", userId);
  }

}

我们常常听说 Python 的代码要比 Java 简短太多,但是从这个例子中我们看到 Java 的代码比 python 的代码也多不了多少,大约只多出 50%。

思考题

为什么 Redis 的事务不能支持回滚?

本文由 mdnice 多平台发布

http://www.yayakq.cn/news/545336/

相关文章:

  • 网站怎么样做不违规厦门网站建设和人才库建设
  • 权威的营销单页网站北京网站优化效果怎样
  • 中山精品网站建设策划书南宁网站建设公司比优建站
  • wordpress 瑜伽课程seo基础优化包括哪些内容
  • 网站建设平台点击进入郑州seo技术服务顾问
  • 汉阳网站推广优化在线二维码制作生成器
  • 如何检测网站的打开速度昆明市哪里有网站建设
  • 做网站的详细流程网店装修图片大全
  • wordpress 快递插件包头seo营销公司
  • 娱乐城网站模板网页设计就是网站开发吗
  • 关于网站建设的问题wordpress 英文 企业网站模板
  • 公司网站制作合同阿里邮箱企业登录入口
  • 福州建站免费模板做城市门户网站怎么发展
  • 织梦五彩婚纱源码网_婚庆策划网站php源码wordpress中文版和英文版
  • 企业网站建设西安买个天猫店多少钱一个
  • 罗湖区做网站的公司网页设计实训报告总结100字
  • 衡东建设局网站正能量不良网站进入窗口免费阅读
  • 网站建设四网合一展示型网站和官网
  • 五指山网站建设收录排名好的发帖网站
  • 姑苏区住房建设局网站远程访问群晖wordpress
  • 网站后端开发软件网络推广培训网站
  • 站长工具综合查询2020黄骅吧招聘信息
  • 网站建设方案免费下载传媒公司的经营范围有哪些
  • 织梦做双语网站wordpress支持的语言
  • 网站制作人员网站搭建流程负责人
  • 辽阳网站推广自己用dw做网站能加声音吗
  • 适合大学生做兼职的网站有哪些路桥建设局网站
  • 网站中弹出广告怎么做深圳关键词排名seo
  • 开发和发布网站的主要流程360网站收录
  • 自己做网站要会什么软件下载邢台网站制作报价多少钱