IMap.unlock() 与 Hazelcast 中的 EntryProcessor 和 MapListener 一起使用时的性能问题

Performance issue in IMap.unlock() when used with along with EntryProcessor and MapListener in Hazelcast

HZ 版本:3.5.3

我在 IMap.unlock(key) 中遇到性能问题,它需要大约 4-5 秒才能完成执行。场景如下:

我有一个 employeeList IMap,它根据员工列表 (ArrayList<Employee>) 存储 companyId。每个值(Arraylist)可能包含 1500000 个员工。

IMap<Integer, ArrayList<Employee>> employeeListMap = hz.getMap("empList");

// adding MapListener for eviction.
employeeListMap.addEntryListener(new SimpleEvictionListener<Integer,
                                               ArrayList<Employee>>(), false);

int companyId = 1;
ArrayList<Employee> empList = new ArrayList<>();
for(int index = 0; index < 1500000; index++)
{
    empList.add(new Employee(index));
}
employeeListMap.set(companyId, empList);

// lock() takes approx 2ms.
employeeListMap.lock(key);

// EDIT: do some business logic associated with this key.

// executeOnKey() takes approx 3ms.
employeeListMap.executeOnKey(companyId, new ListEntryProcessor<Integer, 
                          ArrayList<Employee>>());

// unlock() takes 4-5sec 
employeeListMap.unlock(companyId);
employeeListMap.destroy();

Employee是一个定义如下的POJO。

public class Employee implements Serializable
{
    private static final long serialVersionUID = 1L;
    protected int employeeId;
    protected String name;

    public Employee(int id)
    {
        this.employeeId = id;
        this.name = "name-" + id;
    }

    public int getEmployeeId() 
    {
        return employeeId;
    }

    public void setEmployeeId(int employeeId) 
    {
        this.employeeId = employeeId;
    }

为了添加新员工,我编写了一个条目处理器 SimpleEntryProcessor,它将向列表中添加一个新员工并且 return 为真。

public class ListEntryProcessor<K, V> extends AbstractEntryProcessor<K, V> 
{

    private static final long serialVersionUID = 129712L;
    public ListEntryProcessor()
    {
        // We need to modify the backup entries as well.
        super(true);
    }

    @Override
    public Object process(Entry<K, V> entry) 
    {
        ArrayList<Employee> empList = (ArrayList) entry.getValue();
        empList.add(new Employee(-123));
        entry.setValue((V)empList);
        return true;
    }
}

为了在驱逐时打印钥匙,我已将以下 MapListener 添加到 employeeMap。

public class SimpleEvictionListener<K, V>  implements  
             EntryEvictedListener<K, V>, MapEvictedListener
{
    public void mapEvicted(MapEvent arg0) 
    {
        syso("map got evicted");
    }

    public void entryEvicted(EntryEvent<K, V> arg0) 
    {
        syso("entry got evicted");
    }
}

IMap配置如下

<map name="empList">
    <in-memory-format>OBJECT</in-memory-format>
    <backup-count>0</backup-count>
    <max-idle-seconds>1800</max-idle-seconds>
    <eviction-policy>LRU</eviction-policy>
    <time-to-live-seconds>0</time-to-live-seconds>
    <max-size>51000</max-size>
    <eviction-percentage>30</eviction-percentage>
    <merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicy</merge-policy>
</map>

在这种情况下,IMap.unlock()需要 4-5 秒 完成执行。

当我注释掉代码 employeeListMap.addEntryListener(...)(即没有 MapListener)时,IMap.unlock() 方法只用了 1 毫秒

这是 hazelcast 的未决问题吗?任何指针都会有很大帮助。

注意:我知道我应该将 <employeeId, Employee> 存储在单独的 employee IMap 中,将 <companyId, <list of emp ids> 存储在不同的 companyEmps IMap 中以获得更好的结果。但是,由于代码的遗留性质,这是不可能的。

锁定时间确实很奇怪。但是当您使用 EntryProcessor 时,您不需要应用锁。 EntryProcessor 块条目,因此不会发生并发更新。

我会为这个问题创建一张票。它看起来像一个错误。

您使用的是哪个 HZ 版本?

我已将您的代码片段放入一个 class 中,以便能够轻松尝试:https://gist.github.com/gurbuzali/af8422339bfa81af9750

Hazelcast 中存在一个错误,即使您将 false 传递给 employeeListMap.addEntryListener() 以获取 includeValue 参数,该错误也会序列化该值。 由于您的值太大,问题在您的案例中变得更加明显。

以下是报告的问题和修复 PR。修复将在尚未发布的 3.5.5 中进行,但您可以尝试使用快照 3.5.5-SNAPSHOT

https://github.com/hazelcast/hazelcast/issues/6866

https://github.com/hazelcast/hazelcast/pull/6949