多个程序操作同一张表导致超时的解决方案

当多个程序对同一张表进行操作时,容易出现超时现象,导致数据读取或写入失败。以下是针对此问题的一些解决方法。

一、代码优化

在进行大量数据读写操作时,代码的效率显得尤为重要。因此,进行代码优化是一个有效的解决方法。常用的代码优化方法有:

1、使用缓存,将频繁读写的数据存入缓存。这样可以减少数据库的读写次数,提高程序的效率。


public class CacheHelper {
    private static Map cache = new HashMap();

    public static Object get(String key){
        return cache.get(key);
    }

    public static void set(String key, Object value){
        cache.put(key, value);
    }

    public static void remove(String key){
        cache.remove(key);
    }
}

//使用示例
Object obj = CacheHelper.get("key");
if (obj == null) {
    obj = getDataFromDatabase();
    CacheHelper.set("key", obj);
} 
return obj;

2、批量操作数据库,将多条SQL语句合并为一条。这样可以减少数据库的连接次数,在处理大批量数据时效果更加明显。


public void batchInsert(List data) {
    try {
        Connection conn = getConnection();
        conn.setAutoCommit(false);

        String sql = "insert into TABLE_NAME (col1, col2, col3, ... ) values(?, ?, ?, ...)";
        PreparedStatement ps = conn.prepareStatement(sql);

        for (int i=0; i<data.size(); (exception="" ...="" 

二、分布式锁

在多个程序同时操作一张表时,避免重复写入数据是一个重要的问题。使用分布式锁可以保证在任意时刻只有一个程序对数据进行写操作。

1、使用Redis实现分布式锁:


public class RedisLock {
    private RedisTemplate redisTemplate;

    public RedisLock(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public boolean lock(String key) {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        return valueOperations.setIfAbsent(key, "1");
    }

    public void unlock(String key) {
        redisTemplate.delete(key);
    }
}

//使用示例
RedisLock lock = new RedisLock(redisTemplate);
if (lock.lock("lock_key")) {
    //do something
    lock.unlock("lock_key");
} else {
    //wait or return directly
}

2、使用ZooKeeper实现分布式锁:


public class ZooKeeperLock {
    private ZooKeeper zooKeeper;

    public ZooKeeperLock(String zkUrl) throws IOException, KeeperException, InterruptedException {
        CountDownLatch connectedSignal = new CountDownLatch(1);
        zooKeeper = new ZooKeeper(zkUrl, 1000, new Watcher() {
            public void process(WatchedEvent we) {
                if (we.getState() == Event.KeeperState.SyncConnected) {
                    connectedSignal.countDown();
                }
            }
        });
        connectedSignal.await();
    }

    public boolean lock(String key) throws KeeperException, InterruptedException {
        String path = "/locks/" + key;
        String createdPath = zooKeeper.create(path, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        List siblings = zooKeeper.getChildren("/locks", false);

        //重载排序方法(compareTo是从小到大排序)
        Collections.sort(siblings, new Comparator() {
            public int compare(String s1, String s2) {
                return s1.substring(s1.indexOf("-")+1).compareTo(s2.substring(s2.indexOf("-")+1));
            }
        });

        if (createdPath.equals("/locks/" + siblings.get(0))) {
            return true;
        } else {
            String prevSibling = siblings.get(siblings.indexOf(createdPath.substring("/locks/".length()))-1);
            final CountDownLatch lockSignal = new CountDownLatch(1);
            Stat stat = zooKeeper.exists("/locks/"+prevSibling, new Watcher() {
                public void process(WatchedEvent we) {
                    if (we.getType() == Event.EventType.NodeDeleted) {
                        lockSignal.countDown();
                    } else if (we.getType() == Event.EventType.None) {
                        if (we.getState() == Event.KeeperState.Expired) {
                            lockSignal.countDown();
                        }
                    }
                }
            });
            if (stat != null) {
                lockSignal.await();
            }
            return true;
        }
    }

    public void unlock(String key) throws KeeperException, InterruptedException {
        String path = "/locks/" + key;
        zooKeeper.delete(path, -1);
    }
}

//使用示例
ZooKeeperLock lock = new ZooKeeperLock("zk_url");
if (lock.lock("lock_key")) {
    //do something
    lock.unlock("lock_key");
} else {
    //wait or return directly
}

三、读写分离

当大量的读操作与写操作同时进行时,读写分离是一个有效的解决方法。将读操作与写操作分别交给不同的节点进行处理,可以有效减轻单个数据库节点的负担。

1、使用MySQL实现读写分离:


jdbc:mysql://master_server/database?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&rewriteBatchedStatements=true

jdbc:mysql:loadbalance://master_server,slave_server1,slave_server2/database?loadBalanceConnection=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&rewriteBatchedStatements=true&readFromMasterWhenNoSlaves=true

2、使用MongoDB实现读写分离:


@Bean
public MongoClient mongoClient() {
    MongoClientURI uri = new MongoClientURI("mongodb://username:password@host1,host2,host3/?replicaSet=rs0");
    return new MongoClient(uri);
}

@Bean(name = "mongoTemplate")
public MongoTemplate mongoTemplate() throws Exception {
    MongoTemplate template = new MongoTemplate(mongoClient(), "database_name");
    return template;
}

四、分区表

当数据量过大时,将数据分成几个小的表格是一种很好的解决方法。这样可以避免单个表格过大,导致读写效率下降。

1、使用MySQL实现分区表:


CREATE TABLE sales (
     sale_id INT NOT NULL AUTO_INCREMENT,
     sale_date DATE NOT NULL,
     sale_amount DECIMAL(12,2) NOT NULL,
     PRIMARY KEY (sale_id,sale_date)
) 
PARTITION BY RANGE( YEAR(sale_date) ) (
     PARTITION p0 VALUES LESS THAN (2010),
     PARTITION p1 VALUES LESS THAN (2011),
     PARTITION p2 VALUES LESS THAN (2012),
     PARTITION p3 VALUES LESS THAN MAXVALUE
);

2、使用MongoDB实现分区表:


db.runCommand( { shardCollection: "db.collection", key: { "_id": "hashed" } } );

五、数据库连接池

在进行大量数据读写操作时,数据库连接池是一个非常重要的组件。它可以保证在并发高峰期,数据库连接的利用率最大。

1、使用Druid实现数据库连接池:


@Bean
public DataSource dataSource() {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/test");
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    dataSource.setMaxActive(20);
    dataSource.setInitialSize(1);
    dataSource.setMinIdle(1);
    dataSource.setMaxWait(60000);
    dataSource.setTimeBetweenEvictionRunsMillis(60000);
    dataSource.setMinEvictableIdleTimeMillis(300000);
    dataSource.setValidationQuery("SELECT 1 FROM DUAL");
    dataSource.setTestWhileIdle(true);
    dataSource.setTestOnBorrow(false);
    dataSource.setTestOnReturn(false);
    return dataSource;
}

@Bean
public JdbcTemplate jdbcTemplate() {
    return new JdbcTemplate(dataSource());
}

2、使用HikariCP实现数据库连接池:


@Bean
public DataSource dataSource() {
    HikariConfig config = new HikariConfig();
    config.setDriverClassName("com.mysql.cj.jdbc.Driver");
    config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
    config.setUsername("root");
    config.setPassword("root");
    config.setMaximumPoolSize(20);
    config.setMinimumIdle(1);
    config.setConnectionTestQuery("SELECT 1 FROM DUAL");
    config.setAutoCommit(false);
    config.addDataSourceProperty("cachePrepStmts", "true");
    config.addDataSourceProperty("prepStmtCacheSize", "250");
    config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
    config.addDataSourceProperty("useServerPrepStmts", "true");
    config.addDataSourceProperty("useLocalSessionState", "true");
    config.addDataSourceProperty("rewriteBatchedStatements", "true");
    config.addDataSourceProperty("cacheResultSetMetadata", "true");
    config.addDataSourceProperty("cacheServerConfiguration", "true");
    config.addDataSourceProperty("elideSetAutoCommits", "true");
    config.addDataSourceProperty("maintainTimeStats", "false");
    return new HikariDataSource(config);
}

@Bean
public JdbcTemplate jdbcTemplate() {
    return new JdbcTemplate(dataSource());
}

原创文章,作者:TYJVF,如若转载,请注明出处:https://www.506064.com/n/373700.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
TYJVFTYJVF
上一篇 2025-04-27 15:26
下一篇 2025-04-27 15:26

相关推荐

  • python强行终止程序快捷键

    本文将从多个方面对python强行终止程序快捷键进行详细阐述,并提供相应代码示例。 一、Ctrl+C快捷键 Ctrl+C快捷键是在终端中经常用来强行终止运行的程序。当你在终端中运行…

    编程 2025-04-29
  • Python程序需要编译才能执行

    Python 被广泛应用于数据分析、人工智能、科学计算等领域,它的灵活性和简单易学的性质使得越来越多的人喜欢使用 Python 进行编程。然而,在 Python 中程序执行的方式不…

    编程 2025-04-29
  • 为什么Python不能编译?——从多个方面浅析原因和解决方法

    Python作为很多开发人员、数据科学家和计算机学习者的首选编程语言之一,受到了广泛关注和应用。但与之伴随的问题之一是Python不能编译,这给基于编译的开发和部署方式带来不少麻烦…

    编程 2025-04-29
  • Python栈操作用法介绍

    如果你是一位Python开发工程师,那么你必须掌握Python中的栈操作。在Python中,栈是一个容器,提供后进先出(LIFO)的原则。这篇文章将通过多个方面详细地阐述Pytho…

    编程 2025-04-29
  • Python程序文件的拓展

    Python是一门功能丰富、易于学习、可读性高的编程语言。Python程序文件通常以.py为文件拓展名,被广泛应用于各种领域,包括Web开发、机器学习、科学计算等。为了更好地发挥P…

    编程 2025-04-29
  • Java判断字符串是否存在多个

    本文将从以下几个方面详细阐述如何使用Java判断一个字符串中是否存在多个指定字符: 一、字符串遍历 字符串是Java编程中非常重要的一种数据类型。要判断字符串中是否存在多个指定字符…

    编程 2025-04-29
  • docker-ce-18.03.1.ce-1.el7.centos.x86_64需要pigz这个依赖的解决方案

    当我们在linux centos系统中安装docker-ce-18.03.1.ce-1.el7.centos.x86_64时,有时可能会遇到“nothing provides pi…

    编程 2025-04-29
  • Python购物车程序

    Python购物车程序是一款基于Python编程语言开发的程序,可以实现购物车的相关功能,包括商品的添加、购买、删除、统计等。 一、添加商品 添加商品是购物车程序的基础功能之一,用…

    编程 2025-04-29
  • Python合并多个相同表头文件

    对于需要合并多个相同表头文件的情况,我们可以使用Python来实现快速的合并。 一、读取CSV文件 使用Python中的csv库读取CSV文件。 import csv with o…

    编程 2025-04-29
  • 爬虫是一种程序

    爬虫是一种程序,用于自动获取互联网上的信息。本文将从如下多个方面对爬虫的意义、运行方式、应用场景和技术要点等进行详细的阐述。 一、爬虫的意义 1、获取信息:爬虫可以自动获取互联网上…

    编程 2025-04-29

发表回复

登录后才能评论